[Android] 自定義控件詳解


Android應用開發過程中,固定的一些控件和屬性可能滿足不了開發的需求,所以在一些特殊情況下,我們需要自定義控件與屬性。

一、實現步驟

  1. 繼承View類或其子類 

  2. 復寫view中的一些函數

 3.為自定義View類增加屬性(兩種方式)

 4.繪制控件(導入布局)

 5.響應用戶事件

 6.定義回調函數(根據自己需求來選擇)

二、哪些方法需要被重寫

  • onDraw()

      view中onDraw()是個空函數,也就是說具體的視圖都要覆寫該函數來實現自己的繪制。對於ViewGroup則不需要實現該函數,因為作為容器是“沒有內容“的(但必須實現dispatchDraw()函數,告訴子view繪制自己)。

  • onLayout()

      主要是為viewGroup類型布局子視圖用的,在View中這個函數為空函數。

  • onMeasure()

      用於計算視圖大小(即長和寬)的方式,並通過setMeasuredDimension(width, height)保存計算結果。

  • onTouchEvent

      定義觸屏事件來響應用戶操作。 

還有一些不常用的方法:

  onKeyDown 當按下某個鍵盤時  

  onKeyUp 當松開某個鍵盤時   
 
onTrackballEvent 當發生軌跡球事件時   
 
onSizeChange() 當該組件的大小被改變時   
 
onFinishInflate() 回調方法,當應用從XML加載該組件並用它構建界面之后調用的方法   
 
onWindowFocusChanged(boolean) 當該組件得到、失去焦點時   
onAttachedToWindow() 當把該組件放入到某個窗口時   
 
onDetachedFromWindow() 當把該組件從某個窗口上分離時觸發的方法   
 
onWindowVisibilityChanged(int): 當包含該組件的窗口的可見性發生改變時觸發的方法  

三.自定義控件的三種方式

1. 繼承已有的控件

  當要實現的控件和已有的控件在很多方面比較類似, 通過對已有控件的擴展來滿足要求。

2. 繼承一個布局文件

  一般用於自定義組合控件,在構造函數中通過inflater和addView()方法加載自定義控件的布局文件形成圖形界面(不需要onDraw方法)。

3.繼承view

  通過onDraw方法來繪制出組件界面。

四.自定義屬性的兩種方法

  1.在布局文件中直接加入屬性,在構造函數中去獲得。

布局文件:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" > <com.example.demo.myView android:layout_width="wrap_content" android:layout_height="wrap_content" Text="@string/hello_world" /> </RelativeLayout> 

獲取屬性值:

public myView(Context context, AttributeSet attrs) { super(context, attrs); // TODO Auto-generated constructor stub int textId = attrs.getAttributeResourceValue(null, "Text", 0); String text = context.getResources().getText(textId).toString(); }

2.在res/values/ 下建立一個attrs.xml 來聲明自定義view的屬性。

可以定義的屬性有:

<declare-styleable name = "名稱">  //參考某一資源ID (name可以隨便命名) <attr name = "background" format = "reference" />  //顏色值 <attr name = "textColor" format = "color" />  //布爾值 <attr name = "focusable" format = "boolean" />  //尺寸值 <attr name = "layout_width" format = "dimension" />  //浮點值 <attr name = "fromAlpha" format = "float" />  //整型值 <attr name = "frameDuration" format="integer" />  //字符串 <attr name = "text" format = "string" />  //百分數 <attr name = "pivotX" format = "fraction" />  //枚舉值 <attr name="orientation"> <enum name="horizontal" value="0" /> <enum name="vertical" value="1" /> </attr>  //位或運算 <attr name="windowSoftInputMode"> <flag name = "stateUnspecified" value = "0" /> <flag name = "stateUnchanged" value = "1" /> </attr>  //多類型 <attr name = "background" format = "reference|color" /> </declare-styleable> 

 

  • attrs.xml進行屬性聲明

<?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="myView"> <attr name="text" format="string"/> <attr name="textColor" format="color"/> </declare-styleable> </resources> 
  • 添加到布局文件

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" xmlns:myview="http://schemas.android.com/apk/com.example.demo" > <com.example.demo.myView android:layout_width="wrap_content" android:layout_height="wrap_content" myview:text = "test" myview:textColor ="#ff0000" /> </RelativeLayout> 

這里注意命名空間: 
xmlns:前綴=”http://schemas.android.com/apk/res/包名(或res-auto)”,

前綴:TextColor 使用屬性。

  • 在構造函數中獲取屬性值

public myView(Context context, AttributeSet attrs) {
        super(context, attrs); // TODO Auto-generated constructor stub TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.myView); String text = a.getString(R.styleable.myView_text); int textColor = a.getColor(R.styleable.myView_textColor, Color.WHITE); a.recycle(); }

 或者:

    public myView(Context context, AttributeSet attrs) { super(context, attrs); // TODO Auto-generated constructor stub TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.myView); int n = a.getIndexCount(); for(int i=0;i<n;i++){ int attr = a.getIndex(i); switch (attr) { case R.styleable.myView_text: break; case R.styleable.myView_textColor: break; } } a.recycle(); }

五. 自定義隨手指移動的小球(小例子)

20160503143613554

實現上面的效果我們大致需要分成這幾步

  • 在res/values/ 下建立一個attrs.xml 來聲明自定義view的屬性
  • 一個繼承View並復寫部分函數的自定義view的類
  • 一個展示自定義view 的容器界面

1.自定義view命名為myView,它有一個屬性值,格式為color、

<?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="myView"> <attr name="TextColor" format="color"/> </declare-styleable> </resources>

2.在構造函數獲取獲得view的屬性配置和復寫onDraw和onTouchEvent函數實現繪制界面和用戶事件響應。

public class myView extends View{ //定義畫筆和初始位置 Paint p = new Paint(); public float currentX = 50; public float currentY = 50; public int textColor; public myView(Context context, AttributeSet attrs) { super(context, attrs); //獲取資源文件里面的屬性,由於這里只有一個屬性值,不用遍歷數組,直接通過R文件拿出color值 //把屬性放在資源文件里,方便設置和復用 TypedArray array = context.obtainStyledAttributes(attrs,R.styleable.myView); textColor = array.getColor(R.styleable.myView_TextColor,Color.BLACK); array.recycle(); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); //畫一個藍色的圓形 p.setColor(Color.BLUE); canvas.drawCircle(currentX,currentY,30,p); //設置文字和顏色,這里的顏色是資源文件values里面的值 p.setColor(textColor); canvas.drawText("BY finch",currentX-30,currentY+50,p); } @Override public boolean onTouchEvent(MotionEvent event) { currentX = event.getX(); currentY = event.getY(); invalidate();//重新繪制圖形 return true; } }

  這里通過不斷的更新當前位置坐標和重新繪制圖形實現效果,要注意的是使用TypedArray后一定要記得recycle(). 否則會對下次調用產生影響。 
這里寫圖片描述 

3.把myView加入到activity_main.xml布局里面

<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" xmlns:myview="http://schemas.android.com/apk/res-auto" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="finch.scu.cn.myview.MainActivity"> <finch.scu.cn.myview.myView android:layout_width="match_parent" android:layout_height="match_parent" myview:TextColor="#ff0000" /> </RelativeLayout>

4.最后是MainActivity

public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } }

 

 

 
  • 具體的view要根據具體的需求來,比如我們要側滑刪除的listview我們可以繼承listview,監聽側滑事件,顯示刪除按鈕實現功能。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM