編輯:關於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
設置兩張圖片重疊的模式。在正常的情況下,在已有的圖像上繪圖將會在其上面添加一層新的形狀。如果新的Paint是完全不透明的,那麼它將完全遮擋住下面的Paint;如果它是部分
在項目的開發過程我們離不開圖片,而有時候需要調用本地的圖片,有時候需要調用拍照圖片。同時實現拍照的方法有兩種,一種是調用系統拍照功能,另一種是自定義拍照功能。而本博文目前
剛入門的童鞋肯能都會有一個疑問,Java不是有虛擬機了麼,內存會自動化管理,我們就不必要手動的釋放資源了,反正系統會給我們完成。其實Java中沒有指針的概念,但是指針的使
1、概述之前寫了一個Android 高仿 QQ5.0 側滑菜單效果 自定義控件來襲 ,恰逢QQ5.2又加了一個右側菜單,剛好看了下DrawerLayout,一方面官方的東
本文介紹本文是翻譯自Google 官方課程 Building Apps