View、自定义View

**view绘制**html

一、控件架构

ViewGroup做为 父控件,可包含多个View控件,造成控件树java

上层控件负责下层子控件的测量与绘制,并传递交互事件canvas


二、View的测量---绘制前提

```onMeasure()-MeasureSpec类```架构

1)测量模式:EXACTLY(精确值)ide

AT_MOST (最大值)布局

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

自定义则必须重写onMeasure()方法spa

```~ ... 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; 
}


三、View的绘制

重写onDraw()方法,在Canvas对象上绘制所需对象(在其余地方一般须要建立Canvas canvas=new Canvas(bitmap) ; )设计

四、ViewGroup的测量

管理子View(显示大小)code

**· 当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() )


五、VG绘制

指定VG背景颜色,必须调用onDraw()方法,调用dispatchDraw()绘制子View

六、自定义View

比较重要的回调方法(根据实际须要)

onFinishInflate( ):从XML加载组件后回调

onSizeChanged():组件大小改变时回调

onMeasure():回调该方法进行测量 onLayout():回调肯定显示的位置

onTouchEvent():监听到触摸时间时回调

**实现自定义控件方法**

1)对现有控件拓展

2)经过组合实现新控件

3)重写View实现全新控件

*一、对现有控件进行拓展--可在onDraw()中实现*


//初始化画笔
Paint mPaint=new Paint();
mPaint.setColor(getResources().getColor(~ .blue));
mPaint.setStyle(Paint.Style.FILL);
~...onDraw( ){ //在回调法雷方法前,实现本身的逻辑,对TextView来讲,就是在绘制文本内容前
super.onDraw(); //在 ...后,则在 ... 后 }


二、复合控件--组合

(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();
            }
        });
    }
}
    
    
    

   



三、重写View实现所有控件

 继承View类,重写onDraw()/onMeasure()实现绘制,重写onTouchEvent()实现交互逻辑



七、自定义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();

}