開發(fā)自定義控件的步驟:
創(chuàng)新互聯(lián)主營(yíng)石景山網(wǎng)站建設(shè)的網(wǎng)絡(luò)公司,主營(yíng)網(wǎng)站建設(shè)方案,手機(jī)APP定制開發(fā),石景山h5微信小程序開發(fā)搭建,石景山網(wǎng)站營(yíng)銷推廣歡迎石景山等地區(qū)企業(yè)咨詢
1、了解View的工作原理
2、 編寫繼承自View的子類
3、 為自定義View類增加屬性
4、 繪制控件
5、 響應(yīng)用戶消息
6 、自定義回調(diào)函數(shù)
一、View結(jié)構(gòu)原理
Android系統(tǒng)的視圖結(jié)構(gòu)的設(shè)計(jì)也采用了組合模式,即View作為所有圖形的基類,Viewgroup對(duì)View繼承擴(kuò)展為視圖容器類。
View定義了繪圖的基本操作
基本操作由三個(gè)函數(shù)完成:measure()、layout()、draw(),其內(nèi)部又分別包含了onMeasure()、onLayout()、onDraw()三個(gè)子方法。具體操作如下:
1、measure操作
measure操作主要用于計(jì)算視圖的大小,即視圖的寬度和長(zhǎng)度。在view中定義為final類型,要求子類不能修改。measure()函數(shù)中又會(huì)調(diào)用下面的函數(shù):
(1)onMeasure(),視圖大小的將在這里最終確定,也就是說measure只是對(duì)onMeasure的一個(gè)包裝,子類可以覆寫onMeasure()方法實(shí)現(xiàn)自己的計(jì)算視圖大小的方式,并通過setMeasuredDimension(width, height)保存計(jì)算結(jié)果。
2、layout操作
layout操作用于設(shè)置視圖在屏幕中顯示的位置。在view中定義為final類型,要求子類不能修改。layout()函數(shù)中有兩個(gè)基本操作:
(1)setFrame(l,t,r,b),l,t,r,b即子視圖在父視圖中的具體位置,該函數(shù)用于將這些參數(shù)保存起來;
(2)onLayout(),在View中這個(gè)函數(shù)什么都不會(huì)做,提供該函數(shù)主要是為viewGroup類型布局子視圖用的;
3、draw操作
draw操作利用前兩部得到的參數(shù),將視圖顯示在屏幕上,到這里也就完成了整個(gè)的視圖繪制工作。子類也不應(yīng)該修改該方法,因?yàn)槠鋬?nèi)部定義了繪圖的基本操作:
(1)繪制背景;
(2)如果要視圖顯示漸變框,這里會(huì)做一些準(zhǔn)備工作;
(3)繪制視圖本身,即調(diào)用onDraw()函數(shù)。在view中onDraw()是個(gè)空函數(shù),也就是說具體的視圖都要覆寫該函數(shù)來實(shí)現(xiàn)自己的顯示(比如TextView在這里實(shí)現(xiàn)了繪制文字的過程)。而對(duì)于ViewGroup則不需要實(shí)現(xiàn)該函數(shù),因?yàn)樽鳛槿萜魇恰皼]有內(nèi)容“的,其包含了多個(gè)子view,而子View已經(jīng)實(shí)現(xiàn)了自己的繪制方法,因此只需要告訴子view繪制自己就可以了,也就是下面的dispatchDraw()方法;
(4)繪制子視圖,即dispatchDraw()函數(shù)。在view中這是個(gè)空函數(shù),具體的視圖不需要實(shí)現(xiàn)該方法,它是專門為容器類準(zhǔn)備的,也就是容器類必須實(shí)現(xiàn)該方法;
(5)如果需要(應(yīng)用程序調(diào)用了setVerticalFadingEdge或者setHorizontalFadingEdge),開始繪制漸變框;
(6)繪制滾動(dòng)條;
從上面可以看出自定義View需要最少覆寫onMeasure()和onDraw()兩個(gè)方法。
二、View類的構(gòu)造方法
創(chuàng)建自定義控件的3種主要實(shí)現(xiàn)方式:
1)繼承已有的控件來實(shí)現(xiàn)自定義控件: 主要是當(dāng)要實(shí)現(xiàn)的控件和已有的控件在很多方面比較類似, 通過對(duì)已有控件的擴(kuò)展來滿足要求。
2)通過繼承一個(gè)布局文件實(shí)現(xiàn)自定義控件,一般來說做組合控件時(shí)可以通過這個(gè)方式來實(shí)現(xiàn)。
注意此時(shí)不用onDraw方法,在構(gòu)造廣告中通過inflater加載自定義控件的布局文件,再addView(view),自定義控件的圖形界面就加載進(jìn)來了。
3)通過繼承view類來實(shí)現(xiàn)自定義控件,使用GDI繪制出組件界面,一般無法通過上述兩種方式來實(shí)現(xiàn)時(shí)用該方式。
三、自定義View增加屬性的兩種方法:
1)在View類中定義。通過構(gòu)造函數(shù)中引入的AttributeSet 去查找XML布局的屬性名稱,然后找到它對(duì)應(yīng)引用的資源ID去找值。
案例:實(shí)現(xiàn)一個(gè)帶文字的圖片(圖片、文字是onDraw方法重繪實(shí)現(xiàn))
public class MyView extends View {
private String mtext;
private int msrc;
public MyView(Context context) {
super(context);
}
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
int resourceId = 0;
int textId = attrs.getAttributeResourceValue(null, "Text",0);
int srcId = attrs.getAttributeResourceValue(null, "Src", 0);
mtext = context.getResources().getText(textId).toString();
msrc = srcId;
}
@Override
protected void onDraw(Canvas canvas) {
Paint paint = new Paint();
paint.setColor(Color.RED);
InputStream is = getResources().openRawResource(msrc);
Bitmap mBitmap = BitmapFactory.decodeStream(is);
int bh = mBitmap.getHeight();
int bw = mBitmap.getWidth();
canvas.drawBitmap(mBitmap, 0,0, paint);
//canvas.drawCircle(40, 90, 15, paint);
canvas.drawText(mtext, bw/2, 30, paint);
}
}
布局文件:
?xml version="1.0" encoding="utf-8"?
LinearLayout xmlns:android=""
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
com.example.myimageview2.MyView
android:id="@+id/myView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
Text="@string/hello_world"
Src="@drawable/xh"/
/LinearLayout
屬性Text, Src在自定義View類的構(gòu)造方法中讀取。
2)通過XML為View注冊(cè)屬性。與Android提供的標(biāo)準(zhǔn)屬性寫法一樣。
案例: 實(shí)現(xiàn)一個(gè)帶文字說明的ImageView (ImageView+TextView組合,文字說明,可在布局文件中設(shè)置位置)
public class MyImageView extends LinearLayout {
public MyImageView(Context context) {
super(context);
}
public MyImageView(Context context, AttributeSet attrs) {
super(context, attrs);
int resourceId = -1;
TypedArray typedArray = context.obtainStyledAttributes(attrs,
R.styleable.MyImageView);
ImageView iv = new ImageView(context);
TextView tv = new TextView(context);
int N = typedArray.getIndexCount();
for (int i = 0; i N; i++) {
int attr = typedArray.getIndex(i);
switch (attr) {
case R.styleable.MyImageView_Oriental:
resourceId = typedArray.getInt(
R.styleable.MyImageView_Oriental, 0);
this.setOrientation(resourceId == 1 ? LinearLayout.HORIZONTAL
: LinearLayout.VERTICAL);
break;
case R.styleable.MyImageView_Text:
resourceId = typedArray.getResourceId(
R.styleable.MyImageView_Text, 0);
tv.setText(resourceId 0 ? typedArray.getResources().getText(
resourceId) : typedArray
.getString(R.styleable.MyImageView_Text));
break;
case R.styleable.MyImageView_Src:
resourceId = typedArray.getResourceId(
R.styleable.MyImageView_Src, 0);
iv.setImageResource(resourceId 0 ?resourceId:R.drawable.ic_launcher);
break;
}
}
addView(iv);
addView(tv);
typedArray.recycle();
}
}
attrs.xml進(jìn)行屬性聲明, 文件放在values目錄下
?xml version="1.0" encoding="utf-8"?
resources
declare-styleable name="MyImageView"
attr name="Text" format="reference|string"/attr
attr name="Oriental"
enum name="Horizontal" value="1"/enum
enum name="Vertical" value="0"/enum
/attr
attr name="Src" format="reference|integer"/attr
/declare-styleable
/resources
MainActivity的布局文件:先定義命名空間 xmlns:uview="" (com.example.myimageview2為你
在manifest中定義的包名)
然后可以像使用系統(tǒng)的屬性一樣使用:uview:Oriental="Vertical"
LinearLayout xmlns:android=""
xmlns:uview=""
xmlns:tools=""
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity"
TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hello_world" /
com.example.myimageview2.MyImageView
android:id="@+id/myImageView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
uview:Text="這是一個(gè)圖片說明"
uview:Src="@drawable/tw"
uview:Oriental="Vertical"
/com.example.myimageview2.MyImageView
/LinearLayout
四、控件繪制 onDraw()
五、
六:自定義View的方法
onFinishInflate() 回調(diào)方法,當(dāng)應(yīng)用從XML加載該組件并用它構(gòu)建界面之后調(diào)用的方法
onMeasure() 檢測(cè)View組件及其子組件的大小
onLayout() 當(dāng)該組件需要分配其子組件的位置、大小時(shí)
onSizeChange() 當(dāng)該組件的大小被改變時(shí)
onDraw() 當(dāng)組件將要繪制它的內(nèi)容時(shí)
onKeyDown 當(dāng)按下某個(gè)鍵盤時(shí)
onKeyUp 當(dāng)松開某個(gè)鍵盤時(shí)
onTrackballEvent 當(dāng)發(fā)生軌跡球事件時(shí)
onTouchEvent 當(dāng)發(fā)生觸屏事件時(shí)
onWindowFocusChanged(boolean) 當(dāng)該組件得到、失去焦點(diǎn)時(shí)
onAtrrachedToWindow() 當(dāng)把該組件放入到某個(gè)窗口時(shí)
onDetachedFromWindow() 當(dāng)把該組件從某個(gè)窗口上分離時(shí)觸發(fā)的方法
onWindowVisibilityChanged(int): 當(dāng)包含該組件的窗口的可見性發(fā)生改變時(shí)觸發(fā)的方法
不多說,先上圖:
列舉當(dāng)前目錄下的所有文件,如果是選擇目錄,則不顯示文件,如果是選擇文件,則需要顯示文件。
新建目錄,就是在當(dāng)前路徑下新建目錄,同時(shí)新建后的目錄要能夠及時(shí)顯示在文件列表中。
需要讀寫權(quán)限,添加第三方權(quán)限請(qǐng)求庫:
使用:
DialogFragment與Dialog有一些不同的地方,其中show方法需要傳入FragmentManager
另外需在onCreateVie方法初始化布局,以及獲取到控件
另外就是RecycleView,之所以采用RecycleView,是因?yàn)榘l(fā)現(xiàn)如果用ListView,內(nèi)存會(huì)不斷增加,很難降下來。
其中CommonAdapter繼承自BaseAdapter,是通用的Adapter,兼容ListView:
這一部分邏輯有FileProvider類完成; 這里需要注意的是,有些手機(jī)不支持讀取根目錄,所以改為讀取"/mnt/"作為根目錄就行讀取。
另外跳轉(zhuǎn)目錄都是改變當(dāng)前路徑,然后再刷新數(shù)據(jù)。
同時(shí)在其內(nèi)部定義了FileData類:
文件選擇,可以通過當(dāng)前路徑路徑以及列表索引來唯一確定路徑;都是,當(dāng)跳轉(zhuǎn)目錄后,索引應(yīng)該重置。
這里采用WeakReference記錄選擇的控件,但選擇其他目錄或者文件時(shí),之前的控件需要重置一下狀態(tài)。
該源碼主要用于圖片合成gif或者視頻,其中文件選擇彈窗是自己寫的。感覺這個(gè)彈出應(yīng)該有許多地方可以用到,所以寫下這篇文章,方便以后參考查看。
效果圖:
說明:重寫LinearLayout實(shí)現(xiàn)的ViewPager指示器,可設(shè)置顯示數(shù)目,實(shí)際數(shù)目大于顯示數(shù)目時(shí)可以拖動(dòng)和Fling。course的顏色,高度都寫成了自定義屬性,使用起來非常方便。
屬性:Indicator
tab_color_normal:tab文字不選中顏色
tab_color_light:tab文字選中顏色
cursor_color:course游標(biāo)顏色
cursor_height:course游標(biāo)高度
cursor_offset:course的左右縮進(jìn)(大于0)
IndicatorLayout.java
使用xml
使用java
關(guān)于我:
每個(gè)view的坐標(biāo)系原點(diǎn)為左上角那個(gè)點(diǎn),水平方向?yàn)閤軸,右正左負(fù),豎直方向?yàn)閥軸,下正上負(fù)。
canvas.drawColor //繪制區(qū)域涂上顏色(設(shè)置底色/蒙層)
canvas.drawCircle(float centerX(圓心X坐標(biāo)),float centerY(圓心Y坐標(biāo)),float radius(圓的半徑,單位像素),Paint paint)
canvas.drawBitmap
canvas.drawRect(float left,float top,float right,float bottom,Paint paint) //畫矩形
canvas.drawRect(RecF rect,Paint paint)
canvas.drawRect(Rect rect,Paint paint)
canvas.drawPoint(float x(點(diǎn)X軸坐標(biāo)),float y(點(diǎn)Y軸坐標(biāo)),Paint paint)//畫點(diǎn)
點(diǎn)的大小 -paint.setStrokeWidth(width)
點(diǎn)的形狀 -paint.setStrokeCap(cap)
ROUND(圓形),BUTT(平頭),SQUARE(方頭)
canvas.drawPoints()//批量畫點(diǎn)
canvas.drawOval(float left(左邊界點(diǎn)),float top(上邊界點(diǎn)),float right(右邊界點(diǎn)),float bottom(下邊界點(diǎn)),Paint paint) //畫橢圓
canvas.drawLine(float startX(起點(diǎn)X軸坐標(biāo)),float startY(起點(diǎn)Y軸坐標(biāo)),float stopX(終點(diǎn)X軸坐標(biāo)),float stopY(終點(diǎn)X軸坐標(biāo)),Paint paint) (setStyle對(duì)直線沒有影響)
canvas.drawLines(批量畫線)
canvas.drawRoundRect(float left,float top,float right,float bottom,float rx(圓角的橫向半徑),float ry(圓角的縱向坐標(biāo)),Paint paint)//畫圓角矩形
canvas.drawRoundRect(RectF rect,float rx, float ry,Paint paint)
canvas.drawArc(float left, float top, float right, float bottom, float startAngle(起始角度,順時(shí)針為正,逆時(shí)針為負(fù)), float sweepAngle(弧形劃過角度), boolean useCenter(是否連接到圓心), Paint paint) //繪制弧形或扇形 根據(jù)弧形所在橢圓進(jìn)行繪制
canvas.drawPath() //通過描述路徑的方式來繪制圖形
path.addXxx() —添加子圖形
path.addCircle(x,y,radius,dir(路徑方向:順時(shí)針/逆時(shí)針))
path.xxxTo —畫線
path.lineTo()
path.rLineTo()
path.close() —封閉當(dāng)前圖形
path.setFillType(Path.FillType ft) //設(shè)置填充模式
canvas.drawBitmap(Bitmap bitmap,float left,float top,Paint paint);//畫bitmap
canvas.drawBitmap(Bitmap bitmap,Rect src,RectF dst,Paint paint)
canvas.drawBitmap(Bitmap bitmap,Rect src,Rect dst,Paint paint)
canvas.drawBitmap(Bitmap bitmap,Matrix matrix,Paint paint)
canvas.drawText(String text,float x(起點(diǎn)x坐標(biāo)),float y(起點(diǎn)y坐標(biāo)),Paint paint) //繪制文字
Paint.setStyle //設(shè)置繪制模式
FILL 填充模式(默認(rèn))
STROKE 畫線模式
FILL_AND_STROKE 既畫線又填充
Paint.setStrokeWidth //設(shè)置線條寬度 (僅在style:Stroke、FILL_AND_STROLE下有效)
Paint.setTextSize //設(shè)置文字大小
Paint.setAntiAlias //設(shè)置抗鋸齒開關(guān)
Paint.setTextSize(float textSize)//設(shè)置文字大小
Paint.setStrokeJoin(Paint.Join join) //設(shè)置拐角的形狀
MITER//尖角(默認(rèn))
BEVEL//平角
ROUND//圓角
Paint.setStokeMiter(float miter)//設(shè)置MITER型拐角的延長(zhǎng)線的最大值
設(shè)置顏色
直接設(shè)置顏色
Paint.setColor(int color)
Paint.setARGB(int a,int r,int g,int b)
Paint.setShader(Shader shader) //設(shè)置shader
LinearGradient 線性漸變
RadialGradient 輻射漸變
SweepGradient 掃描漸變
BitmapShader 用bitmap的像素來作為圖形或文字的填充
ComposeShader 混合著色器,多個(gè)shader混合使用
Paint.setColorFilter(ColorFilter colorFilter) //設(shè)置顏色過濾
Paint.setXfermode(Xfermode xfermode) //以要繪制的內(nèi)容為源圖像,以View中已有內(nèi)容作為目標(biāo)圖像,選取一個(gè)PorterDuff.Mode作為繪制內(nèi)容的顏色處理方案。
色彩優(yōu)化
Paint.setDither(boolean dither) //設(shè)置抖動(dòng)來優(yōu)化色彩深度降低時(shí)的繪制效果
Paint.setFilterBitmap(boolean filter) //設(shè)置雙線性過濾優(yōu)化Bitmap放大繪制的效果
可以理解為 由馬賽克變成模糊狀態(tài)
Paint.setPathEffect(PathEffect effect)//使用PathEffect設(shè)置形狀的輪廓效果
CornerPathEffect//把所有的拐角變成圓角
DiscretePathEffect//把線條進(jìn)行隨機(jī)的偏離
DashPathEffect//使用虛線
PathDashPathEffect//使用一個(gè)Path來繪制虛線
SumPathEffect//組合效果
ComposePathEffect//組合效果,組合有先后順序
Paint.setShadowLayer(float radius,float dx,float dy,int shadowColor)//添加陰影
Paint.setMaskFilter(MaskFilter maskfilter)//在繪制層上方的附加效果
BlurMaskFilter //模糊效果
new BlurMaskFilter(float radius(模糊范圍),BlurMaskFilter.Blur style(模糊類型))
EmbossMaskFilter//浮雕效果
new EmbossMaskFilter(float[] direction(光源的方向),float ambient(環(huán)境光強(qiáng)度),float specular(炫光系數(shù)),float blurRadius(光線范圍))
獲取繪制的Path
getFillPath(Path src,Path dst)//實(shí)際path
getTextPath(Stirng text,int start,int end,float x,float y,Path)/getTextPath(char[] text,int index,int count,float x,float y,Path path)//文字的path
drawTextOnPath()//沿一條Path來繪制文字
StaticLayout //繪制文字,支持換行
paint.setFakeBoldText(booleab fakeBoldText)//是否使用偽粗體
paint.setStrikeThruText()//是否加刪除線
paint.setUnderLineText(boolean underlineText)//是否加下劃線
paint.setTextSkewX(float skewX)//設(shè)置文字橫向錯(cuò)切角度
paint.setTextScaleX(float scaleX)//設(shè)置文字橫向放縮
paint.setLetterSpacing(float letterSpacing)//設(shè)置字符間距,默認(rèn)為0
paint.setTextAlign(Paint.Align align)//LEFT、CENTER、RIGHT默認(rèn)為L(zhǎng)EFT
paint.setTextLocale(Locale locale)/paint.setTextLocales(LocaleList locales) //設(shè)置繪制所用的地域
paint.setHinting(int mode)//是否啟用字體微調(diào)
測(cè)量文字尺寸類:
paint.getFontSpacing();//獲取推薦的行距
paint.getFontMetrics();//獲取point的FontMetrics
baseline:基準(zhǔn)線
ascent/descent:普通字符的頂部和底部范圍
top/bottom:限制字型的頂部和底部
leading:行的額外間距,即上一行字的bottm與下一行字的top距離
paint.getTextBounds(String text(測(cè)量的文字),int start(文字的起始位置),int end(文字的結(jié)束位置),Rect bounds(文字顯示范圍的對(duì)象))//獲取文字的顯示范圍
paint.measureText(String text)//測(cè)量文字占用的寬度
measureText()getTextBounds()
paint.getTextWidths(String text,float[] widths)//獲取字符串中每個(gè)字符的寬度,并把結(jié)果填入?yún)?shù)widths
paint.breakText(String text((要測(cè)量的文字),boolean measureForwards(測(cè)量的方向),float maxWidth(寬度上限(超出上限會(huì)截?cái)辔淖?),float[] measuredWidth(用于接受數(shù)據(jù)))//測(cè)量完成后會(huì)把文字寬度賦給measureWidth[0]
paint.getRunAdvance(CharSequence text,int start(文字的起始坐標(biāo)),int end(文字的結(jié)束坐標(biāo)),int contextStart(上下文的起始坐標(biāo)),int ContextEnd(上下文的結(jié)束坐標(biāo)),boolean isRtl(文字的方向),int offset(字?jǐn)?shù)的偏移))//計(jì)算某個(gè)字符處光標(biāo)的x坐標(biāo)
paint.getOffsetForAdvance(CharSequence text, int start, int end, int contextStart, int contextEnd, boolean isRtl, float advance)//計(jì)算出文字中最接近這個(gè)位置的字符偏移量
paint.hasGlyph(String s)//檢查指定的字符串是否是一個(gè)單獨(dú)的字型
canvas.clipRect()//范圍裁剪
canvas.clipPath()//根據(jù)范圍裁剪
canvas.translate(float dx,float dy)//位移
canvas.rotate(float degrees,float px,float py)//旋轉(zhuǎn)
canvas.scale(float sx(橫向縮放倍數(shù)),float sy(縱向縮放倍數(shù)),float px,float py)//縮放
canvas.skew(float sx(x軸的錯(cuò)切系數(shù)),float sy(y軸的錯(cuò)切系數(shù)))//錯(cuò)切
canvas.setMatrix(matrix)//用Matrix直接替換Canvas當(dāng)前的變換矩陣
canvas.concat(matrix)//用Canvas當(dāng)前的變換矩陣和Matrix相乘
Camera.rotate*()//三維旋轉(zhuǎn)
1、super.draw()//總調(diào)度方法
2、super.onDraw()
3、dispatchDraw()//繪制子View的方法
繪制順序:
draw()總調(diào)度方法,view的繪制過程都發(fā)生在draw()方法里
1、背景(drawBackground()不能重寫)-------android:background:/View.setBackgroundXxx()
2、主體(onDraw())
3、子View(dispatchDraw())
4、滑動(dòng)邊緣漸變和滑動(dòng)條(onDrawForeground())-------android:scrollbarXxx/View.setXXXScrollBarXXX()
5、前景(onDrawForeground())-------android:foreground/View.setForeground()
view.animate().translationX()//x軸偏移
1、如果是自定義控件,需要添加setter、getter方法
2、ObjectAnimator.ofXXX()創(chuàng)建ObjectAnimator對(duì)象
3、用start()方法執(zhí)行動(dòng)畫
setDuration(int duration)//設(shè)置動(dòng)畫時(shí)長(zhǎng)
setInterpolator(Interpolator interpolator)//設(shè)置插值器
ViewPropertyAnimator.setListener()/ObjectAnimator.addListener()
ViewPropertyAnimator.setUpdateListener()/ObjectAnimator.addUpdateListener()
ObjectAnimator.addPauseListener()
ViewPropertyAnimator.withStartAction/EndAction()
ArgbEvaluator//顏色漸變動(dòng)畫
PropertyValuesHolder//同一個(gè)動(dòng)畫中改變多個(gè)屬性
PropertyValuesHolders.ofKeyframe()//把同一個(gè)屬性拆分
AnimatorSet//多個(gè)動(dòng)畫配合執(zhí)行
targetSdkVersion=14,硬件加速默認(rèn)開啟
view.setLayerType()
LAYER_TYPE_SOFTWARE:使用軟件來繪制View Layer,繪制到Bitmap,并順便關(guān)閉硬件加速
LAYER_TYPE_HARDWARE:使用GPU來繪制View Layer,繪制到OpenGL texture(如果硬件加速關(guān)閉,那么行為和LAYER_TYPE_SOFTWARE一致)
LAYER_TYPE_NONE:關(guān)閉View Layer
View Layer可以加速無invalidate()(例如動(dòng)畫)時(shí)的刷新效率,但對(duì)于需要調(diào)用invalidate()的刷新無法加速
硬件加速并不支持所有的繪制操作
1、測(cè)量(measure)
View:View在onMeasuer中會(huì)計(jì)算自己的尺寸然后保存
ViewGroup:ViewGroup在onMeasure中會(huì)調(diào)用所有子View的measure讓它們進(jìn)行自我測(cè)量,并根據(jù)子View
計(jì)算出的期望尺寸來計(jì)算他們的事跡尺寸和位置然后保存。
2、布局(layout)
View:無子View所以onLayout不做任何處理
ViewGroup:ViewGroup在onLayout中會(huì)調(diào)用自己所有子View的layout方法,把他們的尺寸、位置傳給他們, 讓他們完成自我布局。
MeasureSpec = mode + size :父類傳遞過來給當(dāng)前View的一個(gè)建議值
MeasureSpec.getMode(int spec)//獲取模式
MeasureSpec.getSize(int spec)//獲取數(shù)值
限制分類:
UNSPECIFIED(不限制)
AT_MOST(限制上限)-wrap_content
EXACTLY(限制固定值)-match_parent/具體值
1、重寫onMeasure來修改已有的View尺寸
(1)、重寫onMeasure方法,調(diào)用super.onMeasure觸發(fā)原有的自我測(cè)量。
(2)、在super.onMeasure下用getMeasureWidth與getMeasureHeigh獲取之前測(cè)量的結(jié)果,使用自己的算法計(jì)算新結(jié)果。
(3)、調(diào)用setMeasureDimension保存新結(jié)果。
2、重寫onMeasure來全新定制自定義View的尺寸
與1區(qū)別,保證計(jì)算的同時(shí),保證結(jié)果滿足父View給出的尺寸限制
(1)重寫onMeasure,計(jì)算出View的尺寸
(2)使用resolve讓子View的計(jì)算結(jié)果符合父View的限制,也可不使用該方法自己定義
3、重寫onMeasure和onLayout來全新定制自定義ViewGroup的內(nèi)部布局
兩個(gè)注意點(diǎn):
子控件間的margin值
1、重寫generateLayoutParams()和generateDefaultLayoutParams()
2、獲取margin值 MarginLayoutParams lp = (MarginLayoutParams )child.getLayoutParams()
子控件間的padding值
1、測(cè)量后直接getPaddingLeft、getPaddingTop、getPaddingRight、getPaddingBottom
重寫onMeasure來計(jì)算內(nèi)部布局
(1)調(diào)用每個(gè)子View的measure來計(jì)算子View的尺寸
結(jié)合layout_xxx和自己可用空間
(2)計(jì)算子View的位置并保存子View的尺寸和位置
(3)計(jì)算自己的尺寸并用setMeasureDimension保存
重寫onLayout來擺放子View
(1)調(diào)用每個(gè)子View的layout,讓他們保存自己的位置和尺寸
view工作原理
觸摸事件
1、ACTION_DOWN:手指剛接觸屏幕,按下去的那一瞬間
2、ACTION_MOVE:手指在屏幕上移動(dòng)
3、ACTION_UP:手指從屏幕上松開的瞬間
事件序列:從ACTION_DOWN - ACTION_UP
ViewGroup:
DispatchTouchEvent
? return true:表示該View內(nèi)部消化掉了所有事件
? return false:表示事件在本層不再繼續(xù)進(jìn)行分發(fā),并交由上層控件的onTouchEvent方法進(jìn)行消費(fèi)
? return super.dispatchTouchEvent(ev):默認(rèn)事件將分發(fā)給本層的事件攔截onInterceptTouchEvent方法 進(jìn)行處理
OnInterceptTouchEvent
? return true:表示將事件進(jìn)行攔截,并將攔截到的事件交由本層控件的onTouchEvent進(jìn)行處理
? return false:表示不對(duì)事件進(jìn)行攔截,事件得以成功分發(fā)到子View
? return super.onInterceptTouchEvent(ev):默認(rèn)表示不攔截該事件,并將事件傳遞給下一層View的 dispatchTouchEvent
OnTouchEvent 默認(rèn)false
? return true:表示onTouchEvent處理完事件后消費(fèi)了此次事件
? return fasle:表示不響應(yīng)事件,那么該事件將會(huì)不斷向上層View的onTouchEvent方法傳遞,直到某個(gè)View的 onTouchEvent方法返回true
? return super.dispatchTouchEvent(ev):表示不響應(yīng)事件,結(jié)果與return false一樣
子View不存在分發(fā):
? DispatchTouchEvent 事件分發(fā)
? OnTouchEvent 默認(rèn)true
如下圖為事件分發(fā)流程圖:
---------------------- 以上總結(jié)部分源自Hencoder教程 ------------------------------
? 組合控件:即由原生控件拼裝而成,不需要自己實(shí)現(xiàn)或者繪制具體的頁面內(nèi)容和效果,常用于標(biāo)題欄TitlleView
? ? eg:
???繼承控件的意思就是,我們并不需要自己重頭去實(shí)現(xiàn)一個(gè)控件,只需要去繼承一個(gè)現(xiàn)有的控件,然后在這個(gè)控件上增加一些新的功能,就可以形成一個(gè)自定義的控件了。這種自定義控件的特點(diǎn)就是不僅能夠按照我們的需求加入相應(yīng)的功能,還可以保留原生控件的所有功能。
熟悉view的繪制原理
1.measure用來測(cè)量View的寬和高。?
2.layout用來確定View在父容器中放置的位置。?
3.draw用來將view繪制在屏幕上
標(biāo)題名稱:android自定控件,Android自定義控件高級(jí)進(jìn)階與精彩實(shí)例
當(dāng)前地址:http://chinadenli.net/article22/dsiiejc.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)頁設(shè)計(jì)公司、外貿(mào)建站、品牌網(wǎng)站建設(shè)、網(wǎng)站收錄、Google、微信小程序
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)
營(yíng)銷型網(wǎng)站建設(shè)知識(shí)