View、自定義View

**view繪製**

1、控件架構

ViewGroup作爲 父控件,可包含多個View控件,形成控件樹

上層控件負責下層子控件的測量與繪製,並傳遞交互事件


2、View的測量---繪製前提

```onMeasure()-MeasureSpec類```

1)測量模式:EXACTLY(精確值)

AT_MOST (最大值)

UNSPECIFIED (不指定,在自定義view中使用)

自定義則必須重寫onMeasure()方法

```~ ... onMeasure(~ ,~ ){ 
setMeasuredDimension( measureWidth(widthMS),measureHeight(~);
}
//參數是MeasureSpec的測量寬度、自定義的寬度方法
~ ..int measureWidth(int measureSpec){
int result=0; 
int specMode=MS.getMode(mS);
int SpecSize=MS.getSize(mS);
if(sM==MS.EXACTLY){
result=specSize;
} else{ result=200; //當不是EXACTLY模式,則需要給默認值if(specMode==MS.AT_MOST){result=Math.min(result,specSize);
//若是AT_MOST模式,則要取出我的指定的大小與SpecSize中最小的}
}
return result; 
}


3、View的繪製

重寫onDraw()方法,在Canvas對象上繪製所需對象(在其他地方通常需要創建Canvas canvas=new Canvas(bitmap) ; )

4、ViewGroup的測量

管理子View(顯示大小)

**· 當VG爲wrap_content,需要對子View遍歷,獲得所有子View大小,從而決定自己的大小**

**·在其他模式,則通過具體的指定值來設置自身的大小**

1)VG在測量時通過遍歷所有子View,從而調用子View的Measure方法來獲得每一個子View的測量結果

2)子View測量後,View的Layout方法設定其放置位置

3)VG執行layout過程時,遍歷調用子View的Layout方法,指定其具體顯示的位置,決定其佈局位置(在自定義VG時,重寫onLayout()方法控制子View顯示位置,若需要支持wrap_content,必須重寫onMeasure() )


5、VG繪製

指定VG背景顏色,必須調用onDraw()方法,調用dispatchDraw()繪製子View

6、自定義View

比較重要的回調方法(根據實際需要)

onFinishInflate( ):從XML加載組件後回調

onSizeChanged():組件大小改變時回調

onMeasure():回調該方法進行測量 onLayout():回調確定顯示的位置

onTouchEvent():監聽到觸摸時間時回調

**實現自定義控件方法**

1)對現有控件拓展

2)通過組合實現新控件

3)重寫View實現全新控件

*1、對現有控件進行拓展--可在onDraw()中實現*


//初始化畫筆
Paint mPaint=new Paint();
mPaint.setColor(getResources().getColor(~ .blue));
mPaint.setStyle(Paint.Style.FILL);
~...onDraw( ){ //在回調法雷方法前,實現自己的邏輯,對TextView來說,就是在繪製文本內容前
super.onDraw(); //在 ...後,則在 ... 後 }


2、複合控件--組合

(1)定義屬性


//在res-values下創建attrs.xml的屬性定義文件
<resource>
   <declare-styleablename="Topbar">
      <attr name ="title" format="string" />
      <attrname="leftBackground"format="color|reference"/>
   </>
</>……


(2)創建控件繼承VG`


//獲取自定義屬性
TypedArray ta=context.obtainStyledAttributes(attrs,.styleable.Topbar);
mLeftTextColor=ta.getColor(R.~~.Topbar_leftTextColor, 0);
ta.recycle( ); //獲取完 要釋放


(3)組合控件(左-中-右)


//動態添加控件--addView ,添加到定義的Topbar中,設置屬性值
	mLeftButton=new Button(context);
	mTitleView=new TextView(~);
	……
	mLeftButton.setTextColor(mLeftTextColor);
	……
//爲組件元素設置相應的佈局元素
	mLeftParams=new LayoutParams(L~P~.WRAP_CONTENT,L~P~.M~P~);
	mLeftParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT,TRUE);
//添加到VG
	addView(mLeftButton ,mLeftParams);



利用回調機制設計按鈕的點擊事件
 <1>定義接口---UI模板類中創建方法
public interface topbarClickListener{
	void leftClick();
	void rightClick();
}


<2>暴露接口給調用者---模板方法中,增加點擊事件(調用接口點擊方法)
mRightButton.setOnclickListener(new OnClickListener(){
~……
...onClick(){mListener.rightClick();}
}
public void setOnTopbarClickListener(topbarClickListener mListener){
this.mListener=mListener;
}
<3>實現接口回調——匿名內部類,主類調用
mTopbar.setOnTopbarClickListener(new ~……);

(4)引用UI模板
指定命名空間:xmlns:myspace="……/res-auto"
使用屬性:myspace:leftBackground="……"
寫到佈局文件中,其他地方可用include引用:<com.xys.mytopbar.Topbar ……>


public class Topbar extends RelativeLayout {

    //設置自定義控件的父類佈局的參數
    private LayoutParams titleParams_lp;
    private LayoutParams leftParams_lp;
    private LayoutParams rightParams_lp;

    //自定義的屬性
    private String title_txt;
    private int title_color;
    private float titleTextSize;

    private String left_txt;
    private int leftText_color;
    private Drawable leftBackground;

    private String right_txt;
    private int rightText_color;
    private Drawable rightBackground;
    //自定義的控件
    private Button left_btn,right_btn;  //左右按鈕
    private TextView title_tv;  //標題

    private topbarClickListener listener;   //自定義的接口實例對象
    //自定義接口
    public interface topbarClickListener{
        public void leftClick();
        public void rightClick();   //抽象點擊方法
    }
    //自定義的點擊方法---將回調回來的匿名內部類參數給系統的ClickListener()
    public void setOnTopbarClickListener(topbarClickListener listener){//傳來一個參數--接口的對象
        this.listener=listener;
    }

    public Topbar(Context context, AttributeSet attrs) {
        super(context, attrs);
        TypedArray ta=context.obtainStyledAttributes(attrs,R.styleable.Topbar);
                //將自定義的屬性從中獲取出來
        title_txt=ta.getString(R.styleable.Topbar_title);
        title_color=ta.getColor(R.styleable.Topbar_titleColor,0);
        titleTextSize=ta.getDimension(R.styleable.Topbar_titleTextSize,0);

        left_txt=ta.getString(R.styleable.Topbar_leftText);
        leftText_color=ta.getColor(R.styleable.Topbar_leftTextColor,0);
        leftBackground=ta.getDrawable(R.styleable.Topbar_leftBackground);

        right_txt=ta.getString(R.styleable.Topbar_rightText);
        rightText_color=ta.getColor(R.styleable.Topbar_rightTextColor,0);
        rightBackground=ta.getDrawable(R.styleable.Topbar_rightBackground);

        ta.recycle();   //回收
//實例化控件
        title_tv=new TextView(context);
        left_btn=new Button(context);
        right_btn=new Button(context);

 //將控件與自定義的屬性關聯
        title_tv.setText(title_txt);
        title_tv.setTextColor(title_color);
        title_tv.setTextSize(titleTextSize);
        title_tv.setGravity(Gravity.CENTER);    //直接設定居中對齊

        left_btn.setText(left_txt);
        left_btn.setTextColor(leftText_color);
        left_btn.setBackground(leftBackground);


        right_btn.setText(right_txt);
        right_btn.setTextColor(rightText_color);
        right_btn.setBackground(rightBackground);

        setBackgroundResource(R.color.colorWhite);
    /**自定義控件的父類佈局參數*/
        titleParams_lp=new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.WRAP_CONTENT);   //佈局寬 高、 對齊方式
        titleParams_lp.addRule(RelativeLayout.CENTER_IN_PARENT);
            //左邊佈局自適應,居左對齊
        leftParams_lp=new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
                ViewGroup.LayoutParams.WRAP_CONTENT);
        leftParams_lp.addRule(RelativeLayout.ALIGN_PARENT_LEFT);

        rightParams_lp=new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
                ViewGroup.LayoutParams.WRAP_CONTENT);
        rightParams_lp.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
    /**將自定義的控件放進父佈局中---也就是設定它們的佈局位置  */
        addView(title_tv,titleParams_lp);
        addView(left_btn,leftParams_lp);
        addView(right_btn,rightParams_lp);
/**自定義的點擊事件------利用回調機制----------------*/
        left_btn.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View view) {
                listener.leftClick();
                //此時將傳回來的點擊事件(通過 匿名內部類參數)傳遞給left_btn的監聽接口
            }
        });
        right_btn.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View view) {
                listener.rightClick();
            }
        });
    }
}

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Topbar topbar= (Topbar) findViewById(R.id.tb_title);
        topbar.setOnTopbarClickListener(new Topbar.topbarClickListener() {
            @Override
            public void leftClick() {
                Toast.makeText(MainActivity.this,"我是back",Toast.LENGTH_SHORT).show();
            }

            @Override
            public void rightClick() {
                Toast.makeText(MainActivity.this,"我是go",Toast.LENGTH_SHORT).show();
            }
        });
    }
}



3、重寫View實現全部控件

 繼承View類,重寫onDraw()/onMeasure()實現繪製,重寫onTouchEvent()實現交互邏輯



7、自定義ViewGroup

重寫onMeasure()對子View的測量,父onLayout()確定子位置,onTouchEvent()響應事件
eg:實現類似ScrollView功能

      (1)放置子View--利用遍歷的方式對子View測量、設定位置

~onMeasure(){
~ int  count=getChildCount();
for(int i=0 ; i<count ; i++){
View childView=getChildAt(i);
measureChild(childView ,widthMeasureSpec,heightMeasureSpec);
}
}
//每個子View佔一屏高度,則VG高度爲子View個數乘以屏幕高度
   ~onLayout(){
~   int childCount=getChildCount();
     MarginLayoutParams mlp=()getLayoutParams();//設置VG高度
     mlp.height=mScreenHeight*childCount;
     setLayoutParams(mlp);
     for(int i=0; i<childCount; i++){
View child=getChildAt(i);
if(child.getVisibility()!=View.GONE)
child.layout(1,i*mScreenHeight,r,(i+1)*mScreenHeight);
     }
 }

(2)添加響應事件才能滑動(滑動到一段距離會跳一整頁)

~onTouchEvent(){
int y=()event.getY();

}





 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章