您的位置:首页 > 其它

自定义View <0> 继承现有的控件

2016-03-28 10:10 148 查看
自定义View的 第一种形式继承现有的UI控件:实现特定功能,例如事件拦截,重新绘制。(继承某个控件,例如EditText,需要两个构造方法)

0:自定义View的步骤和使用:

继承View或者View的子类
声明构造方法
View(Context c) 这个构造方法在代码中创建控件的时候使用
View(Context ct,AttributeSet set)这个构造在布局xml文件中创建控件的时候自动的调用,如果自定义控件没有这个构造方法,就会抱错。报错地址:http://blog.csdn.net/rodulf/article/details/50915600,这个含有AttributeSet
的构造方法,应用于布局创建控件,这个AttribteSet attrs 就是XML里面的属性,通过这个属性传给控件,不然控件怎么知道高度时多少,宽度是多少,

1:继承已有的控件的方式

    如果要拦截,就重写onInterceptTouchEvent

    如果要重新绘制,那么就是重写onDraw()方法,onDraw方法是一个回调方法,当Android 要显示当前的控件到屏幕上的时候,就会回调这个方法,让控件自己把自己长什么样子画到屏幕上
canvas就是画布的意思,当控件在画布上面画完之后,最后由系统贴到屏幕上,记住是贴到屏幕上面的
如果上面的onDraw里面删除了super.onDraw(canvas)的话,是不会显示的,调整控件的显示:通过空间的onDraw 方法来修改,super.onDraw(canvas) 代表原有的空间显示方式;
canvas.drawArc可以用来画饼图

        canvas.drawBitmap();可以切图,按照等比切,drawBitmap还可以用来做穿衣和试衣的软件 。

/!!! 记住了一定不能在onDraw方法里面进行对象的创建,这样非常影响性能。

2:

+++++++++++

1: 游戏,股票,涂鸦

2:办公统计,柱状统计。

3: 电子书

4:听力测试

像大神一样写代码

-----------------------------------------

提升的地方:-----------------------------

-----------------------------------------

内置的UI空间和布局无法满足需求的时候,就需要进行外观,操作都是自定义的一些控件,

这个时候就要进行自定义View

-----------------------------------------

自定义View 的方式:

1)继承现有的UI空间:实现特定功能

2)将多个空间组合,形成新的自定义View:瀑布流,Radio动态指示器

3)完全自定义回执:自己来话出来外观,自己实现事件

自定义View 的步骤和使用:

1:继承View 或者View 的子类

2:申明构造方法

3:View(Context c)这个构造在代码中创建空间的时候使用

4:View(Context ct,AttributeSet set)这个构造在布局xml 文件中创建空间的时候自动调用。

如果自定义空间没有博啊汗带有AttributeSet的参数的构造方法,就会报异常。

新建一个CusomerView1

创建一个包widget

新建一个java类SimpleView

public class SimpleView extends EditText {

    /**

     * 任何控件,只有一个参数的构造方法,应用于代码创建控件

     * 例如 ImageView imageView = new ImageView(context);

     * @param context

     */

    public SimpleView(Context context) {

        super(context);

    }

    /**

     * 包含AttributeSet 的构造方法,应用于在布局文件中加载控件的情况。

     * AttributeSet 实际上就是包含了布局中的XML指定的属性;通过这个属性传给空间。

     * 这个方法在布局中创建空间的时候,是必须要调用和存在的,不然会抛出异常。

     * @param context

     * @param atts

     */

    public SimpleView(Context context,AttributeSet atts){

        super(context,atts);

    }

}

main 的xml

<!--包含自定义空间或者第三方控件或者Android Support 中的空间

    都可以通过类的全路劲来包含

    -->

    <com.kodulf.customerview1.widget.SimpleView

        android:layout_width="match_parent"

        android:layout_height="wrap_content"

        android:textColor="#EE3344"

        android:hint="请输入您的内容"/>

修改:

public class SimpleView extends LinearLayout {

<com.kodulf.customerview1.widget.SimpleView

        android:layout_width="match_parent"

        android:layout_height="wrap_content"

        android:orientation="horizontal">

        <TextView

            android:text="自定义View 里面添加一个View"

            android:layout_width="wrap_content"

            android:layout_height="wrap_content"

            />

        </com.kodulf.customerview1.widget.SimpleView>

在修改

public class SimpleView extends LinearLayout {

   <com.kodulf.customerview1.widget.SimpleView

            android:layout_width="match_parent"

            android:layout_height="_match_parent"

            android:background="#EE3344"/>

早上第二课

----------------------------------------------

----------------------------------------------

新建NotePad java 

shift+F6 修改名字

/**

 * 自定义View 案例1:继承已有空间的方式,实现记事本输入界面:

 */

public class NotePadView extends EditText{

    public NotePadView(Context context){

        //super(context);

        //通常,一个参数的构造,可以调用自身两个参数的

        this(context,null);

    }

    public NotePadView(Context context, AttributeSet attrs){

        super(context,attrs);

    }

}-------------------------------------------------------

重写onDraw()方法

去韩国,按照某个图片去话,这个图片就是onDraw();

    /**

     * 这个方法是一个回调方法,当Android 要显示当前控件到屏幕上的时候

     * 就会调用这个额方法啊,让控件自己把自己长什么样子画到屏幕上

     *

     * @param canvas Canvas 相当于一个画布,当空间在画布上面画完之后,最后由系统贴到屏幕上

     *               记住是贴到屏幕上的。

     */

    @Override

    protected void onDraw(Canvas canvas) {

        

        super.onDraw(canvas);

        //TODO: 绘制内容:

    }

如果上面的onDraw里面删除了super.onDraw(canvas)的话,是不会显示的

调整控件的显示:通过空间的onDraw 方法来修改

super.onDraw(canvas) 代表原有的空间显示方式;

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

drawArc()还可以画饼图,drawBitmap还可以切图,按照等比切,

                        //drawBitmap穿衣试衣的软件,还可以

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

//android 代码当中,所有的和尺寸,坐标相关的单位,都是像素px

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

Carvas 绘制的时候,都需要设置Paint,

/!!!!注意android 控件的onDraw方法,是进制创建任何对象的;

光标显示一次,创建一次,所以不允许创建任何对象。

设置成员变量Paint

/**

     * 用于控制画线的样式;

     * 通常不要再onDraw创建,

     * 需要创建一个单独的初始化方法,

     */

    private Paint linePaint;

    /**

     * 用于初始化绘制时使用的各种对象数据;

     * @param context

     * @param set

     */

private void init(Context context,AttributeSet set){

            linePaint = new Paint();
linePaint.setColor(Color.RED);

    }

然后再在构造方法调用;

public NotePadView(Context context, AttributeSet attrs){

        super(context, attrs);

        init(context,attrs);

    }

更新:

protected void onDraw(Canvas canvas) {

        //对于已有空间而言,super.onDraw 代表原有内容的显示;

        super.onDraw(canvas);

        //TODO: 绘制内容:只要使用Canvase的绘制,就可以实现显示了;

        //Android 控件中,自身的左上角永远是(0,0)

        //右下角 就是(width,height)坐标轴

        //android 的整个屏幕,也是左上角(0,0)

        //android 代码当中,所有的和尺寸,坐标相关的单位,都是像素px

        //从(startX,startY)到(endX,endY)

        //!!!!注意android 控件的onDraw方法,是进制创建任何对象的;

        canvas.drawLine(

                0,//startX

                50,//startY

                200,//endX

                50,

        linePaint);//endY

                //drawArc()还可以画饼图,drawBitmap还可以切图,按照等比切,

                        //drawBitmap穿衣试衣的软件,还可以

    }

更新:

以空间的宽度来画:

        //继承已有空间:充分利用以后空间的方法和属性,完成功能

        //1.获取控件的宽度,

        int width =getWidth();

        canvas.drawLine(0,50,width,50,linePaint);

更新:

        //继承已有空间:充分利用以后空间的方法和属性,完成功能

        //1.获取控件的宽度,

        int width =getWidth();

        //2.对于EditText子类而言,需要获取一行的文本的高度,

        //因为可以设置textSize,通过 getLineHeight();来得到

        int lineHeight = getLineHeight();

        canvas.drawLine(0, lineHeight, width, lineHeight, linePaint);

这个时候会出现,会出现线在字体上面

解决方法如下:

android:background="#FFF"

//3. 在绘制的时候,需要考虑一个空间内部的padding 信息

        // 默认的情况下EditText包含顶部Padding

空间绘制的时候的注意事项:

1:需要考虑空间自身的padding,padding 影响了计算坐标的位置

更新:

//1.获取控件的宽度,

        int width =getWidth();

        //2.对于EditText子类而言,需要获取一行的文本的高度,

        //因为可以设置textSize,通过 getLineHeight();来得到

        int lineHeight = getLineHeight();

        //3. 在绘制的时候,需要考虑一个空间内部的padding 信息

        // 默认的情况下EditText包含顶部Padding

        //设置android:background="#FFF" 可以解决

        int paddingTop = getPaddingTop();

        canvas.drawLine(0, lineHeight+paddingTop, width, lineHeight+paddingTop, linePaint);

   

更新:

        //4。根据行数来绘制线段,

        //获取当当前输入框,实际内容的行数,

        int lineCount = getLineCount();

        for(int i=0;i<lineCount;i++){

            canvas.drawLine(0,paddingTop+lineHeight*(i+1),width,paddingTop+lineHeight*(i+1),linePaint);

        }

UPDATE:

int lineCount = getLineCount();

        for(int i=0;i<lineCount;i++){

            int lineY = paddingTop + lineHeight * (i + 1);

            canvas.drawLine(0, lineY,width, lineY,linePaint);

        }

更新:

//5. 获取高度,来第一次绘制的时候就绘制整个屏幕,需要减去paddingtop 和padding bottom

        int height = getHeight();

        //获取padding bottom 计算实际内容高度

        int paddingBottom = getPaddingBottom();

        int num = (height - paddingBottom - paddingTop)/lineHeight;

        int lineCount = getLineCount();

        lineCount = Math.max(lineCount,num);

        for(int i=0;i<lineCount;i++){

            int lineY = paddingTop + lineHeight * (i + 1);

            canvas.drawLine(0, lineY,width, lineY,linePaint);

        }

下午:

------------------------------------------------------------

------------------------------------------------------------

在onDraw方法,不允许创建对象:

在onDraw方法,里面打印Log。

这个方法什么也不动,它每隔0.5秒绘制一次。

和光标的闪烁一样的频率。

如果在这里面

new int[1024*1024] 

移动的时候也会分配,所以这个会让程序非常非常慢。

所以不要再onDraw方法创建对象。

Android 自己的操作系统,正式的绘制的频率是16ms绘制一次。

每一个控件都有自己的绘制频率。

例如EditText 0.5s一次。它用在光标的闪烁。

--------------------------------------------------------------

--------------------------------------------------------------

自定义的属性

我们想要加入

lineColor="#0F0" 

这个时候需要我们自己添加:

在values 文件夹,添加一个values 文件叫做attr.xml

btw:

format 的num 可以用来形容orientation 这样的属性

<?xml version="1.0" encoding="utf-8"?>

<resources>

    <!--自定义属性的声明-->

    <!--name 通常推荐是类名,这样好区别

    可以认为是给某一个对应的空间声明属性

    -->

    <declare-styleable name="NotePadView">

        <!--attr代表定义的属性名称,相当于NotePadView内部支持属性定义-->

            <attr name="lineColor" format="color"/>

        <!--定义一个名称为lineHeight的属性,内容是尺寸-->

        <attr name="lineHeigth" format="dimension"/>

    </declare-styleable>

</resources>

这个时候就可以添加 app:lineColor="#0F0"

通常命名空间的名称是app,是自动生成的,实际上就是xml命名空间;

在main的xml 里面自动导入了:

xmlns:app="http://schemas.android.com/apk/res-auto"

这个时候设置还没有完全的好。

还需要在代码中获取自定义属性;

更新init 如下:

private void init(Context context,AttributeSet set){

            linePaint = new Paint();

        //linePaint.setColor(Color.RED);

        //TODO: 从AttributeSet获取属性配置,来设置横线的属性

        int lineColor = Color.RED;

        if(set!=null) {

            //获取属性集合中的lineColor属性

            //获取自定义属性操作步骤:

            //1.先获取指定的,在attrs.xml中定义的那个属性集合declare-style

            //代表的内容

            //obtainStyledAttributes(); 获取自定义属性,需要AttributeSet才可以

            TypedArray array = context.obtainStyledAttributes(

                    set,//包含了xml 中当前控件的所有属性 android:,app:这两种开头的

                    R.styleable.NotePadView//获取针对NotePadView 的所有属性。

            );

            //获取颜色属性,

            //参数一:index,就是android中自定义属性的索引,

            int color = array.getColor(

                    R.styleable.NotePadView_lineColor,//通过常量已经定义好了index了

                    Color.RED

            );

            linePaint.setColor(color);

            //TypedArray 对象在使用完之后,必须要被回收

            
float dimension = array.getDimension(R.styleable.NotePadView_lineHeigth, 5.5f);

            linePaint.setStrokeWidth(dimension);

array.recycle();

        }

}

------------------------------------------------------------

----------------------------------------------------------------

组合控件,事件分发。

组合方式的自定义View

listView 就是一个组合布局

ViewPager 也是一个组合布局

AutoComplementTextView,Spinner 也都是的。

常见的第三方控件:

1:瀑布流,

2:拨号盘

3:侧滑菜单

4:Path按钮,

5:水纹进度

6:事件滚轮

---------------------------------------------------------------------

----------------------------------------------------------------------

继承ViewGroup 以及ViewGroup 的子类

新建一个java 文件AlphaIndicator

/**

 * 通过组合的形式,实现字模选择的功能

 * 采用组合控件的形式。

 */

public class AlphaIndicator extends LinearLayout {

    public AlphaIndicator(Context context) {

        //super(context); 这个也行,下面的也行

        this(context,null);

    }

    public AlphaIndicator(Context context, AttributeSet attrs) {

        super(context, attrs);

        init(context,attrs);

    }

    private void init(Context context,AttributeSet attrs){

            //TODO:初始化各种内容和各种属性

        //1.添加A-Z 26个字母

        char ch;

        for (int i = 0; i <26; i++) {

            ch =(char)('A'+i);

            TextView  textView = new TextView(context);

            textView.setText(Character.toString(ch));

            //关于layoutParams,通过LayoutParams 可以给一个控件设置相应的布局属性,

            //控件添加到哪一种布局中,就是用这个布局的LayoutParams的对象来设置。

            //例如控件添加到LinearLayout ,那么个给这个空间设置的就是

            //LinearLayout.LayoutParams 对应的就是android:layout_xxx

            LinearLayout.LayoutParams lp = new LayoutParams(

                    //32,//像素单位的实际数值,或者是WRAP_CONTENT,MATCH_PARENT

                    ViewGroup.LayoutParams.MATCH_PARENT,

                    0,//0高度,因为使用权重,让控件填满布局,

                    1//weight 对应layout_weight="1"

            );

            textView.setLayoutParams(lp);//设置布局参数

            //设置textSize 属性

            textView.setTextSize(TypedValue.COMPLEX_UNIT_SP,20);

            //添加进

            addView(textView);

        }

    }

}

<com.kodulf.customerview1.widget.AlphaIndicator

        android:layout_width="wrap_content"

        android:layout_height="match_parent"

        android:orientation="vertical"

        ></com.kodulf.customerview1.widget.AlphaIndicator>

更新完善:

//设置Android:gravity

            textView.setGravity(Gravity.CENTER);

            textView.setTextColor(Color.RED);

--------------------------------------------------------------

-----------------------------------------------------------------------

组合控件里面的事件处理

1:控件的触摸事件分为两种形式:

1)控件自身的onTouchEvent(...),控件自己来实现和处理触摸事件,是一种默认的处理

2) 给空间设置的setOnTouchListener();外部来设置触摸事件的处理【表情】可以覆盖onTouchEvent();

默认的处理;

2:通常对于自定义空间而言,可以采用onTouchEvent,外部将setOnTouchListener 留给外部代码;

3:触摸事件的类型:DOWN,MOVE,UP等,

4:ACTION_DOWN, 如果

//!!!!!如果ACTION_DOWN的状态,返回true 代表当前控件需要继续

                //执行其他状态的监控;如果返回false,或者super调用,

                //都不会再收到触摸事件了;

--------------------------------------------------------------------------------------

    /**

     * 空间自身处理触摸屏幕的事件

     * @param event

     * @return true 代表当前时间处理完成,芙蓉起不要再处理;false,代表芙蓉起还可以继续处理

     */

    @Override

    public boolean onTouchEvent(MotionEvent event) {

        //action 代表触摸的操作状态。

        int action = event.getAction();

        String type="";

        switch (action){

            case MotionEvent.ACTION_DOWN:

                //所有的触摸屏幕操作都是从ACTION_DOWN开始的,也就是手指按下开始

                //!!!!!如果ACTION_DOWN的状态,返回true 代表当前控件需要继续

                //执行其他状态的监控;如果返回false,或者super调用,

                //都不会再收到触摸事件了;

                type="DOWN";

                break;

            case MotionEvent.ACTION_MOVE:

                //手指移动

                type="MOVE";

                break;

            case MotionEvent.ACTION_UP:

                type="UP";

                //手指离开屏幕

                break;

        }

        Log.d("151222MY", type);

        return super.onTouchEvent(event);

    }

上面只会显示DOWN

//!!!!!如果ACTION_DOWN的状态,返回true 代表当前控件需要继续

                //执行其他状态的监控;如果返回false,或者super调用,

                //都不会再收到触摸事件了;

--------------------------------------------------------

更新:

/**

     * 空间自身处理触摸屏幕的事件

     * @param event

     * @return true 代表当前时间处理完成,芙蓉起不要再处理;false,代表芙蓉起还可以继续处理

     */

    @Override

    public boolean onTouchEvent(MotionEvent event) {

---》        boolean ret = false;

        //action 代表触摸的操作状态。

        int action = event.getAction();

        String type="";

        switch (action){

            case MotionEvent.ACTION_DOWN:

                //所有的触摸屏幕操作都是从ACTION_DOWN开始的,也就是手指按下开始

                //!!!!!如果ACTION_DOWN的状态,返回true 代表当前控件需要继续

                //执行其他状态的监控;如果返回false,或者super调用,

                //都不会再收到触摸事件了;

                type="DOWN";

---》                ret=true;

                break;

            case MotionEvent.ACTION_MOVE:

                //手指移动

                type="MOVE";

                break;

            case MotionEvent.ACTION_UP:

                type="UP";

                //手指离开屏幕

                break;

        }

        Log.d("151222MY", type);

        //return super.onTouchEvent(event);

---》        return ret;

    }

------------------------------------------------------------------------------------------

    /**

     * 使用成员变量,进行上一次选中位置的保存,

     * 便面多次选中线通的位置,

     * 因为mover的动作会执行多次。

     */

private int currentPosition = -1;

    /**

     * 空间自身处理触摸屏幕的事件

     * @param event

     * @return true 代表当前时间处理完成,芙蓉起不要再处理;false,代表芙蓉起还可以继续处理

     */

    @Override

    public boolean onTouchEvent(MotionEvent event) {

        boolean ret = false;

        //action 代表触摸的操作状态。

        int action = event.getAction();

        //触摸事件包含x,y

        float ex = event.getX();

        float ey = event.getY();

        //Log.d("151222MY","x:"+ex+" y:"+ey);

        //TODO:事件相对于手机屏幕的x,y如何获取

        String type="";

        switch (action){

            case MotionEvent.ACTION_DOWN:

                //所有的触摸屏幕操作都是从ACTION_DOWN开始的,也就是手指按下开始

                //!!!!!如果ACTION_DOWN的状态,返回true 代表当前控件需要继续

                //执行其他状态的监控;如果返回false,或者super调用,

                //都不会再收到触摸事件了;

                type="DOWN";

                //因为每次按下 是新的触摸事件刘晨给,那么续重位置清空,

                currentPosition=-1;

                ret=true;

                break;

            case MotionEvent.ACTION_MOVE:

                //手指移动

                type="MOVE";

                int count = getChildCount();//获取内部的控件个数,根据空间个数,找到手机放在那个位置上了

                int position =-1;

                for(int i=0;i<count;i++){

                    View view = getChildAt(i);

//                    float viewX = view.getX();

//                    float viewY = view.getY();

                    float viewX = view.getLeft();

                    float viewY = view.getTop();

                    int viewRight = view.getRight();

                    int viewBottom = view.getBottom();

                    if(ex>=viewX&&ex<=viewRight){

                        if(ey>=viewY&&ey<=viewBottom){

                           //当前点中了,这个控件

                            position=i;

                            break;

                        }

                    }

                }

                if(position>-1&&position!=currentPosition){

                   // Toast.makeText(getContext(),"选中:"+position,Toast.LENGTH_SHORT).show();

                    Log.d("151222MY","选中:"+position);

                    currentPosition=position;

                }

                break;

            case MotionEvent.ACTION_UP:

                type="UP";

                //手指离开屏幕

                break;

        }

        //Log.d("151222MY", type);

        //return super.onTouchEvent(event);

        return ret;

    }

-----------------------------------------------------------------

更新:

if(position>-1&&position!=currentPosition){

                    View v = getChildAt(position);

                    String alpha =(String)v.getTag();

                   // Toast.makeText(getContext(),"选中:"+position,Toast.LENGTH_SHORT).show();

                    //Log.d("151222MY","选中:"+position);

                    Log.d("151222MY","选中:"+alpha);

                    currentPosition=position;

                }

更新 添加textView.setTag(ch);到下面的方法中。

private void init(Context context,AttributeSet attrs){

            //TODO:初始化各种内容和各种属性

        //1.添加A-Z 26个字母

        char ch;

        for (int i = 0; i <26; i++) {

            ch =(char)('A'+i);

            TextView  textView = new TextView(context);

            textView.setText(Character.toString(ch));

            textView.setTag(ch);
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: