Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> 自定義View

自定義View

編輯:關於Android編程

1.繼承View/SurfaceView重寫onDraw方法

這種方法主要用於實現一些不規則的效果,即這種效果不方便通過布局的組合方式來達到,往往需要靜態或者動態的顯示一些不規則的圖形。很顯然

這需要通過繪制的方式來實現,即重寫onDraw方法。采用這種方式需要自己支持wrap_content,並且padding也需要自己處理。

2.繼承ViewGroup派生特殊的Layout

這種方法主要用於實現自定義的布局,即除了LinearLayout,RelativeLayout,FrameLayout這幾種系統的布局之外,我們需要重新定義一種新布局,

當某種效果看起來很像幾種View組合在一起的時候,可以采用這種方式實現。采用這種方式稍微復雜一些,需要合適的處理ViewGroup的

測量布局這兩個過程,並同時處理子元素的測量和布局過程。

3.繼承特定的View(如TextView,ImageView等)

這種方法比較常見,一般用於擴展某種已有的View的功能,比如TextView,這種方法的實現比較簡單,不需要自己支持wrap_content和padding等。

4.繼承特定的ViewGroup(如LinearLayout,FrameLayout)

這種方法也比較常見,當某種效果看起來很像幾種View組合起來的時候可以采用這種方法來實現,采用這種方法不需要自己處理ViewGroup的

測量和布局這兩個過程。

需要注意這種方法和方法2的區別,一般來說方法2能實現的效果方法4也能實現,兩者的主要區別在於方法2更加接近View的底層。

以上就是自定義View的四種方法,自定義View講究的是靈活性,一種效果可能多種方式可以實現,我們需要做的是找到一種代價小最高效的方法去實現。

自定義View過程中需要注意的問題

要准確的實現自定義View,我們需要處理一些為題,這些問題可能會影響Viwq的正常使用,有些可能會導致內存洩露等問題。

1.讓View支持wrap_content

繼承的View或者ViewGroup如果不在onMeasure對wrap_content進行特殊處理,那麼當外界使用wrap_content時,會無法達到預期的效果

2.如果有必要,讓你的View支持padding

繼承View的控件,如果不在onDraw中處理padding,那麼padding屬性將無法起作用,同時繼承ViewGroup的控件也需要在

onMeasure和onLayout中考慮padding和magin對其效果的影響

3.盡量不要在View中使用Handler,沒必要

View內部本身已經提供了Post系列的辦法,完全可以替代handler的作用,除非你很明確需要使用Handler來發送消息

4.View中如果有線程或者動畫,需要及時停止,參考View#onDetachedFromWindow

如果有線程或者動畫需要停止,那麼onDetachedFromWindow是一個很好的時機,當包含此View的Activity退出或者當前View被remove時,View的

onDetachedFromWindow方法會被調用,和此方法對應的是onAttachedToWindow,當包含此View的Activity啟動是View的onAttachedToWindow方法將會調用。

同時當View不可見時我們也需要停止線程和動畫,如果不及時處理這種問題,很有可能會造成內存洩露。

5.View帶有滑動嵌套情形時,需要處理好滑動沖突

自定義View示例

1.繼承View

首先建立一個屬性文件,聲明了一個自定義屬性集合,在這個集合中你可以添加許多自己需要的屬性,其格式類型是name = 屬性名 format = 屬性值的類型



    
        
    

然後是在View的構造方法中解析自定義的屬性值並做處理

public CircleView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        // TODO Auto-generated constructor stub
        //加載自定義屬性集合circleview
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.circleview);
        //從集合中獲取設置的屬性值
        mColor = a.getColor(R.styleable.circleview_circle_color, Color.RED);
        //解析完畢釋放資源
        a.recycle();
        init();
    }

最後在布局文件中使用自定義屬性
需要注意的是一定要通過命名空間去使用自定義屬性,即一定要先聲明才能使用該屬性



    

一個簡單的自定義View代碼示例:

package com.example.myroundviewactivity;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;

public class CircleView extends View {

    private int mColor = Color.RED;

    //抗鋸齒
    private Paint mPaint  = new Paint(Paint.ANTI_ALIAS_FLAG); 

    public CircleView(Context context) {
        super(context);
        // TODO Auto-generated constructor stub
        init();
    }

    public CircleView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        // TODO Auto-generated constructor stub
        //自定義屬性
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.circleview);
        mColor = a.getColor(R.styleable.circleview_circle_color, Color.RED);
        a.recycle();
        init();
    }

    public CircleView(Context context, AttributeSet attrs) {
       this(context,attrs,0);
    }

    private void init(){
        mPaint.setColor(mColor);
    }


    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        // TODO Auto-generated method stub
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //給View添加wrap_content支持
        int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
        if (widthSpecMode == MeasureSpec.AT_MOST && heightSpecMode == MeasureSpec.AT_MOST) {
            setMeasuredDimension(200, 200);
        }else if(widthSpecMode == MeasureSpec.AT_MOST){
            setMeasuredDimension(200, heightSpecSize);
        }else if(heightSpecMode == MeasureSpec.AT_MOST){
            setMeasuredDimension(widthSpecSize, 200);
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        // TODO Auto-generated method stub
        super.onDraw(canvas);
        //使自定義View支持padding屬性
        final int paddingleft = getPaddingLeft();
        final int paddingright = getPaddingRight();
        final int paddingtop = getPaddingTop();
        final int paddingbottom = getPaddingBottom();
        int width = getWidth()-paddingleft-paddingright;
        int height = getHeight()-paddingtop-paddingbottom;
        int radius = Math.max(width, height)/2;
        canvas.drawCircle(paddingleft+width/2, paddingtop+height/2, radius, mPaint);
    }
}

2.繼承ViewGroup派生特殊的Layout

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved