• Android笔记——Android自定义控件


    目录:

     

    1.自定义控件概述

    01_什么是自定义控件

      Android系统中,继承Android系统自带的View或者ViewGroup控件或者系统自带的控件,并在这基础上增加或者重新组合成我们想要的效果。

    02_为什么用自定义控件

      系统控件无法满足需求时,需要自定义控件。

      1. 系统的控件在不同手机长得不一样,我们希望在不同手机实现相同的效果;
      2. 有些手机上的控件长得不好看,希望好看一些。
      3. 系统控件的功能有限,需要在基础上增加功能。

    03_怎么用自定义控件-三种方式

    1.使用系统控件,重新组合,实现自定义的效果,案例有:
           优酷环形菜单、广告条循环滚动(Viewpager)、下拉菜单(spinner)、下拉框(PopupWindow、ListView)

    2.自己定义一个类继承View ,实现特定的效果,案例有:
           自定义开关按钮、水波纹效果

    3.自己定义一个类继承ViewGroup,实现特定的效果,案例有:
           仿ViewPager的效果实现 、 仿网易侧滑菜单

    4.自定义属性:给自己的控件,添加自己的属性,通过demo了解系统解析属性的过程,
    并给上一个例子开关按钮,添加新属性。

    04_Android常用控件回顾

        Android本身提供了很多控件,如:
        文本控件    TextView和EditText;
        图片控件    ImageView
        按钮控件    Button和ImageButton
        进度条       ProgressBar
        单选按钮    RadioButton和RadioGroup
        复选按钮    CheckBox
        状态开关按钮ToggleButton
        时钟控件    AnalogClock和DigitalClock
        日期与时间选择控件DatePicker和TimePicker等。
              . . .

      使用原则尽量使用系统的控件,在系统控件没法达到我们的需求的时候才需要自定义控件。再定义控件会带来工作量,例如修改bug.


      文本控件TextView 和EditText
        TextView 控件继承自 View 类。TextView控件的功能是向用户显示文本内容,TextView不允许编辑。
        EditText控件继承自 TextView。EditText与TextView 最大的不同是 EditText是可以编辑的 


      图片控件ImageView
        ImageView 控件负责显示图片,其图片来源既可以是资源文件的id,也可以是Drawable对象或 Bitmap 对象,还可以是 内容提供者(Content Provider)的Uri.

      


      按钮控件Button 和 ImageButton
          Button控件继承自 TextView 类,Button 的用法比较简单,主要是为 Button 设置一个点击事件监听器,并在编写按钮点击事件的处理代码。
        ImageButton 控件 继承自 ImageView。
        ImageButton与Button相同之处:都用于响应按钮的点击事件
        不同之处:ImageButton只能显示图片;Button用于显示文字  
        

      进度条ProgressBar
        ProgressBar继承自 View,用于显示正在运行的状态。有两种显示形式:一种是环形显示只用于显示状态,没有具体的进度。第二种是水平显示,可以显示具体  的进度。
        通过设置不同的Style显示不同的样式:
      style="?android:attr/progressBarStyleLarge"        环形样式
      style="?android:attr/progressBarStyleHorizontal"    水平样式

      


      单选按钮 RadioButton 和复选按钮 CheckBox
        CheckBox 和RadioButton 都继承自CompoundButton,都只有选中和未选中两种状态,可以通过checked属性来设置。
        不同的是RadioButton 是单选按钮,在一个RadioGroup中只能有一个RadioButton按钮处于选中状态;CheckBox 则可以有多个按钮被选中。  

        


      状态开关按钮ToggleButton
        ToggleButton 控件是继承自 CompoundButton。ToggleButton 的状态只能是选中和未选中,并且需要为不同的状态设置不同的显示文本。除了继承自父类的一  些属性和方法之外,ToggleButton 也具有一些自己的属性。

                               


      时钟控件AnalogClock 和 DigitalClock
        AnalogClock继承自 View,用于显示模拟时钟只显示时针和分针。
        DigeitalClock 继承自 TextView。用于显示数字时钟可精确到秒。 时钟控件比较简单,只需要在布局文件中声明控件即可。

                              



      日期选择器 DatePicker 和时间选择器 TimePicker

        DatePicker 继承自FrameLayout类,日期选择控件的主要功能是向用户提供包含年、月、日的日期数据,并允许用户对其修改。如果要捕获这个修改,可以  为  DatePicker添加 onDateChangedListener 监听器。
        TimePicker 同样继承自FrameLayout 类。时间选择控件向用户显示一天中的时间,可以为24小时制,可以为AM/PM 制,并允许用户进行修改。如果要捕获用  户的修改事件,需要为TimePicker 添加OnTimeChangedListener 监听器

                                 
      知识链接:
        android WheelView组件(滑轮组件)的使用 : http://www.myexception.cn/android/1236819.html

        


      系统提供的控件虽然很丰富,但是,还远远不够。有的时候我们必须要自己定义控件来满足我们的要求。下面的案例,详细分析自定义控件的使用:

    2.优酷效果

      
      运行演示做好的优酷菜单效果,并且讲解实现思路;因为现在优酷已经更换界面,引用此界面主要为讲解自定义控件实现的思想。 

      优酷菜单就是使用系统控件,重新组合,来实现自定义的效果的。

      01_优酷布局


        1_创建工程YukuMenuDemo,图片全部拷贝到drawable-hdpi目录下

        2_实现三个圆环-最里面的圆环   

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >
    
        <RelativeLayout
            android:id="@+id/level1"
            android:layout_centerHorizontal="true"
            android:layout_alignParentBottom="true"
            android:background="@drawable/level1"
            android:layout_width="100dip"
            android:layout_height="50dip" >
        </RelativeLayout>
    
    </RelativeLayout>    

        3_实现三个圆环-中间园环     

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >
    
        <RelativeLayout
            android:id="@+id/level2"
            android:layout_width="180dip"
            android:layout_height="90dip"
            android:layout_alignParentBottom="true"
            android:layout_centerHorizontal="true"
            android:background="@drawable/level2" >
        </RelativeLayout>
    
        <RelativeLayout
            android:id="@+id/level1"
            android:layout_width="100dip"
            android:layout_height="50dip"
            android:layout_alignParentBottom="true"
            android:layout_centerHorizontal="true"
            android:background="@drawable/level1" >
        </RelativeLayout>
    
    </RelativeLayout>

        4_实现三个圆环-最外环

          这里要说明下,相对布局里的是有焦点获取先后要求的,level1放在最下面,才能先获得焦点。

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >
    
        <RelativeLayout
            android:id="@+id/level3"
            android:layout_width="280dip"
            android:layout_height="140dip"
            android:layout_alignParentBottom="true"
            android:layout_centerHorizontal="true"
            android:background="@drawable/level3" >
        </RelativeLayout>
    
        <RelativeLayout
            android:id="@+id/level2"
            android:layout_width="180dip"
            android:layout_height="90dip"
            android:layout_alignParentBottom="true"
            android:layout_centerHorizontal="true"
            android:background="@drawable/level2" >
        </RelativeLayout>
    
        <RelativeLayout
            android:id="@+id/level1"
            android:layout_width="100dip"
            android:layout_height="50dip"
            android:layout_alignParentBottom="true"
            android:layout_centerHorizontal="true"
            android:background="@drawable/level1" >
        </RelativeLayout>
    
    </RelativeLayout>

        5_最里环的的图标

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >
    
        <RelativeLayout
            android:id="@+id/level3"
            android:layout_width="280dip"
            android:layout_height="140dip"
            android:layout_alignParentBottom="true"
            android:layout_centerHorizontal="true"
            android:background="@drawable/level3" >
        </RelativeLayout>
    
        <RelativeLayout
            android:id="@+id/level2"
            android:layout_width="180dip"
            android:layout_height="90dip"
            android:layout_alignParentBottom="true"
            android:layout_centerHorizontal="true"
            android:background="@drawable/level2" >
        </RelativeLayout>
    
        <RelativeLayout
            android:id="@+id/level1"
            android:layout_width="100dip"
            android:layout_height="50dip"
            android:layout_alignParentBottom="true"
            android:layout_centerHorizontal="true"
            android:background="@drawable/level1" >
    
            <ImageView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerInParent="true"
                android:src="@drawable/icon_home" />
        </RelativeLayout>
    
    </RelativeLayout>

        6_中间环的图标

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >
    
        <RelativeLayout
            android:id="@+id/level3"
            android:layout_width="280dip"
            android:layout_height="140dip"
            android:layout_alignParentBottom="true"
            android:layout_centerHorizontal="true"
            android:background="@drawable/level3" >
        </RelativeLayout>
    
        <RelativeLayout
            android:id="@+id/level2"
            android:layout_width="180dip"
            android:layout_height="90dip"
            android:layout_alignParentBottom="true"
            android:layout_centerHorizontal="true"
            android:background="@drawable/level2" >
    
            <ImageView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentBottom="true"
                android:layout_margin="10dip"
                android:src="@drawable/icon_search" />
    
            <ImageView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerHorizontal="true"
                android:layout_marginTop="5dip"
                android:src="@drawable/icon_menu" />
    
            <ImageView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentBottom="true"
                android:layout_alignParentRight="true"
                android:layout_margin="10dip"
                android:src="@drawable/icon_myyouku" />
        </RelativeLayout>
    
        <RelativeLayout
            android:id="@+id/level1"
            android:layout_width="100dip"
            android:layout_height="50dip"
            android:layout_alignParentBottom="true"
            android:layout_centerHorizontal="true"
            android:background="@drawable/level1" >
    
            <ImageView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerInParent="true"
                android:src="@drawable/icon_home" />
        </RelativeLayout>
    
    </RelativeLayout>

        7_最外环的图标的左边部分

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >
    
        <RelativeLayout
            android:id="@+id/level3"
            android:layout_width="280dip"
            android:layout_height="140dip"
            android:layout_alignParentBottom="true"
            android:layout_centerHorizontal="true"
            android:background="@drawable/level3" >
    
            <ImageView
                android:id="@+id/channel1"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentBottom="true"
                android:layout_marginBottom="10dip"
                android:layout_marginLeft="10dip"
                android:src="@drawable/channel1" />
    
            <ImageView
                android:id="@+id/channel2"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_above="@id/channel1"
                android:layout_alignLeft="@id/channel1"
                android:layout_marginLeft="20dip"
                android:layout_marginBottom="10dip"
                android:src="@drawable/channel2" />
    
            <ImageView
                android:id="@+id/channel3"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_above="@id/channel2"
                android:layout_alignLeft="@id/channel2"
                android:layout_marginBottom="8dp"
                android:layout_marginLeft="35dp"
                android:src="@drawable/channel3" />
    
            <ImageView
                android:layout_marginTop="10dip"
                android:id="@+id/channel4"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerHorizontal="true"
                android:src="@drawable/channel4" />
        </RelativeLayout>
    
        <RelativeLayout
            android:id="@+id/level2"
            android:layout_width="180dip"
            android:layout_height="90dip"
            android:layout_alignParentBottom="true"
            android:layout_centerHorizontal="true"
            android:background="@drawable/level2" >
    
            ...............
        </RelativeLayout>
    
        <RelativeLayout
            android:id="@+id/level1"
            android:layout_width="100dip"
            android:layout_height="50dip"
            android:layout_alignParentBottom="true"
            android:layout_centerHorizontal="true"
            android:background="@drawable/level1" >
    
           ...............
    
        </RelativeLayout>
    
    </RelativeLayout>

        8_最外环的图标的右边部分

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >
    
        <RelativeLayout
            android:id="@+id/level3"
            android:layout_width="280dip"
            android:layout_height="140dip"
            android:layout_alignParentBottom="true"
            android:layout_centerHorizontal="true"
            android:background="@drawable/level3" >
    
            <ImageView
                android:id="@+id/channel1"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentBottom="true"
                android:layout_marginBottom="10dip"
                android:layout_marginLeft="10dip"
                android:src="@drawable/channel1" />
    
            <ImageView
                android:id="@+id/channel2"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_above="@id/channel1"
                android:layout_alignLeft="@id/channel1"
                android:layout_marginBottom="10dip"
                android:layout_marginLeft="20dip"
                android:src="@drawable/channel2" />
    
            <ImageView
                android:id="@+id/channel3"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_above="@id/channel2"
                android:layout_alignLeft="@id/channel2"
                android:layout_marginBottom="8dp"
                android:layout_marginLeft="35dp"
                android:src="@drawable/channel3" />
    
            <ImageView
                android:id="@+id/channel4"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerHorizontal="true"
                android:layout_marginTop="10dip"
                android:src="@drawable/channel4" />
    
            <ImageView
                android:id="@+id/channel7"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentBottom="true"
                android:layout_alignParentRight="true"
                android:layout_marginBottom="10dip"
                android:layout_marginRight="10dip"
                android:src="@drawable/channel7" />
    
            <ImageView
                android:id="@+id/channel6"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_above="@id/channel7"
                android:layout_alignRight="@id/channel7"
                android:layout_marginBottom="10dip"
                android:layout_marginRight="20dip"
                android:src="@drawable/channel6" />
    
            <ImageView
                android:id="@+id/channel5"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_above="@id/channel6"
                android:layout_alignRight="@id/channel6"
                android:layout_marginBottom="10dip"
                android:layout_marginRight="35dip"
                android:src="@drawable/channel7" />
        </RelativeLayout>
    
             ................
    
             ................
    
    </RelativeLayout>

     02_优酷代码实现

        1_初始化三环的控件,并设置icom_menu和icom_menu的点击事件

    public class MainActivity extends Activity implements OnClickListener {
    
        private RelativeLayout level1;
        private RelativeLayout level2;
        private RelativeLayout level3;
    
        private ImageView icon_home;
        private ImageView icon_menu;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            level1 = (RelativeLayout) findViewById(R.id.level1);
            level2 = (RelativeLayout) findViewById(R.id.level2);
            level3 = (RelativeLayout) findViewById(R.id.level3);
            icon_home = (ImageView) findViewById(R.id.icon_home);
            icon_menu = (ImageView) findViewById(R.id.icon_menu);
    
            icon_home.setOnClickListener(this);
            icon_menu.setOnClickListener(this);
        }
        @Override
        public void onClick(View v) {
            switch (v.getId()) {
            case R.id.icon_home://相应home的点击事件
    
                break;
    
            case R.id.icon_menu://相应menu的点击事件
                break;
            }
        }

        2_三级菜单的显示和隐藏

    private boolean isLevel3Show = true;
    
    @Override
        public void onClick(View v) {
            switch (v.getId()) {
            case R.id.icon_home:// 相应home的点击事件
    
                break;
    
            case R.id.icon_menu:// 相应menu的点击事件
                if (isLevel3Show) {
                    Tools.hideView(level3);
                    isLevel3Show = false;
                } else {
                    Tools.showView(level3);
                    isLevel3Show = true;
                }
                break;
            }
        }

        旋转原理画图分析:    

      

        旋转工具类代码:  

    /**
     * @author m
     *
     */
    public class Tools {
    
        public static void hideView(View view) {
            /**
             * fromDegrees 从多少度开始
             * toDegrees 旋转到度
             * pivotX 中心点x坐标
             * pivotY 中心点y坐标
             */
            RotateAnimation ra = new RotateAnimation(0, 180, view.getWidth()/2, view.getHeight());
            //播放时常
            ra.setDuration(500);
            //停留在播放完成状态
            ra.setFillAfter(true);
            view.startAnimation(ra);
        }
    
        public static void showView(View view) {
            RotateAnimation ra = new RotateAnimation(180, 360, view.getWidth() / 2,
                    view.getHeight());
            ra.setDuration(500);
            ra.setFillAfter(true);
            view.startAnimation(ra);
        }
    }


        3_二级菜单的显示和隐藏

    @Override
        public void onClick(View v) {
            switch (v.getId()) {
            case R.id.icon_home:// 相应home的点击事件
                if (isLevel2Show) {
                    //如果二级菜单式显示的,隐藏二级菜单
                    Tools.hideView(level2);
                    //判断三级菜单的状态,如果是显示,同时也隐藏三级菜单
                    if(isLevel3Show){
                        Tools.hideView(level3);
                    }
                    isLevel2Show = false;
                } else {
                    //如果二级才能使隐藏的,那么显示二级菜单
                    Tools.showView(level2);
                    isLevel2Show = true;
                }
    
                break;
    
            case R.id.icon_menu:// 相应menu的点击事件
                ..................
                break;
            }
        }

        4_设置延迟动画setStartOffset()方法和代码重构      

    /**
     * @author m
     * 
     */
    public class Tools {
    
        public static void hideView(View view) {
            hideView(view, 0);
        }
    
        public static void showView(View view) {
            showView(view, 0);
        }
    
        /**
         * 延迟显示
         * 
         * @param view
         * @param i
         */
        public static void showView(View view, int i) {
            RotateAnimation ra = new RotateAnimation(180, 360, view.getWidth() / 2,
                    view.getHeight());
            ra.setDuration(500);
            ra.setFillAfter(true);
            ra.setStartOffset(i);
            view.startAnimation(ra);
        }
    
        /**
         * 延迟隐藏
         * 
         * @param view
         * @param i
         *            延迟隐藏的时间
         */
        public static void hideView(View view, int i) {
            /**
             * fromDegrees 从多少度开 toDegrees 旋转到度 pivotX x坐标 pivotY y坐标
             */
            RotateAnimation ra = new RotateAnimation(0, 180, view.getWidth() / 2,
                    view.getHeight());
            // 播放时常
            ra.setDuration(500);
            // 停留在播放完成状态
            ra.setFillAfter(true);
            ra.setStartOffset(i);
            view.startAnimation(ra);
    
        }
    }

        5._监听手机menu按键实现菜单隐藏和显示

            多数安卓手机支持menu,但小米手机就不支持。通过观察我们知道,如果都显示:点击menu键,分别隐藏这三级菜单;如果没显示:点击menu键,则显示一二级菜单。

    @Override
        public boolean onKeyDown(int keyCode, KeyEvent event) {
            if (keyCode == KeyEvent.KEYCODE_MENU) {
    
                if (isLevel1Show) {
                    // 如果一级菜单式显示的,那么隐藏 一级菜单
                    Tools.hideView(level1);
                    isLevel1Show = false;
                    // 同时判断 隐藏二级、三级菜单
                    if (isLevel2Show) {
                        Tools.hideView(level2, 200);
                        isLevel2Show = false;
                        if (isLevel3Show) {
                            Tools.hideView(level3, 300);
                            isLevel3Show = false;
                        }
                    }
                } else {
                    // 如果一级菜单式隐藏的,那么就要显示一级菜单
                    Tools.showView(level1);
                    isLevel1Show = true;
                    // 同时要显示二级菜单
                    Tools.showView(level2,200);
                    isLevel2Show = true;
    
                }
    
                return true;
            }
            return super.onKeyDown(keyCode, event);
        }

     03_优酷效果的完成和bug修复

        bug描述:一二三级菜单全部隐藏状态,再点击手机菜单位置,二三级菜单会显示

        

        解决:用ViewGroup和View的区别来解决bug

    /**
     * @author m
     * 
     */
    public class Tools {
    
        public static void hideView(ViewGroup view) {
            hideView(view, 0);
        }
    
        public static void showView(ViewGroup view) {
            showView(view, 0);
        }
    
        /**
         * 延迟显示
         * 
         * @param view
         * @param i
         */
        public static void showView(ViewGroup view, int startOffset) {
            RotateAnimation ra = new RotateAnimation(180, 360, view.getWidth() / 2,
                    view.getHeight());
            ra.setDuration(500);
            ra.setFillAfter(true);
            ra.setStartOffset(startOffset);
            view.startAnimation(ra);
    
            // view.setVisibility(View.VISIBLE);
            // view.setEnabled(true);
         //遍历孩子的个数
    for (int i = 0; i < view.getChildCount(); i++) { view.getChildAt(i).setEnabled(true); } } /** * 延迟隐藏 * * @param view * @param i * 延迟隐藏的时间 */ public static void hideView(ViewGroup view, int startOffset) { /** * fromDegrees 从多少度开 toDegrees 旋转到度 pivotX x坐标 pivotY y坐标 */ RotateAnimation ra = new RotateAnimation(0, 180, view.getWidth() / 2, view.getHeight()); // 播放时常 ra.setDuration(500); // 停留在播放完成状态 ra.setFillAfter(true); ra.setStartOffset(startOffset); view.startAnimation(ra); // view.setVisibility(View.GONE); // view.setEnabled(false); for (int i = 0; i < view.getChildCount(); i++) { view.getChildAt(i).setEnabled(false); } } }

    3.广告条和首页推荐

      


      1_广告条ViewPage的介绍


          1_创建工程名:

            首页影片推广效果,包名为:com.bokeyuan.viewpager,并且拷贝图片到drawable-hdpi目录

          2_写布局文件:

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >
    
        <android.support.v4.view.ViewPager
            android:id="@+id/viewpager"
            android:layout_width="match_parent"
            android:layout_height="200dip" />
    
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_alignBottom="@id/viewpager"
            android:background="#33000000"
            android:gravity="center_horizontal"
            android:orientation="vertical"
            android:padding="5dip" >
    
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="三个火枪手"
                android:textColor="#ffffff"
                android:textSize="18sp" />
    
            <LinearLayout
                android:id="@+id/ll_point_group"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="5dip"
                android:orientation="horizontal" >
            </LinearLayout>
        </LinearLayout>
    
    </RelativeLayout>

          3.实例化ViewPager和关联其源代码:

            代码实例化:

    public class MainActivity extends Activity {
        
        private ViewPager viewpager;
        private LinearLayout ll_point_group;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            viewpager = (ViewPager) findViewById(R.id.viewpager);
            ll_point_group = (LinearLayout) findViewById(R.id.ll_point_group);
        }    
    
    }

            关联源代码:
              1.删除工程里面的Android Depandencies,删除后会报错,不要理会。看下面

                

              2.添加libs目录下的Android-support-v4.jar包
                选中-->右键-->build path-->add to build path

              3.关联源代码
                目录:C:androidadt-bundle-windows-x86_64-20130219sdkextrasandroidsupportv4srcjava
                点击ViewPager类,出现图标;

                

                大家对于v4包都已经很熟悉了,现在在新建android项目时,v4包是默认导入的。v7包出来没多长时间,用的人也不多,主要对3.0以下版本

                提供ActionBar支持,以及SearchView,PopupMenu等控件的支持。因为一些开源框架已经实现对3.0以下版本ActionBar的支 持,所以v7包的

                使用意义也不是很大。

              知识拓展:

                如果jar包导入错误,怎么修改呢?
              右键工程---->properties---->Java Build Path --->Libraries-->选择android-support-v4.jar展开---->Editor--->External Folder

              4.ViewPager的原理
                
              能显示很多页面,者些页面可以是图片也可以是布局文件。


          4_设置图片资源ID和图片标题集合和准备ImageView列表数据

      // 图片资源ID
        private final int[] imageIds = { 
                R.drawable.a,
                R.drawable.b, 
                R.drawable.c,
                R.drawable.d,
                R.drawable.e };
    
        // 图片标题集合
        private final String[] imageDescriptions = {
                "巩俐不低俗,我就不能低俗",
                "扑树又回来啦!再唱经典老歌引万人大合唱", 
                "揭秘北京电影如何升级", 
                "乐视网TV版大派送", 
                "热血屌丝的反杀" };
    
    
    
       //准备数据
            imageList = new ArrayList<ImageView>();
            for(int i=0;i<imageIds.length;i++){
                ImageView imageView = new ImageView(this);
                imageView.setBackgroundResource(imageIds[i]);                             imageList.add(imageView);
            }



          5_为ViewPager设置适配器

    private class MyPagerAdapter extends PagerAdapter {
    
            @Override
            public int getCount() {
                // 页面或者图片的总数
                return imageList.size();
            }
    
            /**
             * 功能:给ViewPager添加指定的view
             * container 就是ViewPager,其实就是容器。
             * position 具体页面或者图片的位置
             */
            @Override
            public Object instantiateItem(ViewGroup container, int position) {
                System.out.println("instantiateItem=="+position);
                View view = imageList.get(position);
                container.addView(view);
                //返回的值,不一定是View ,也可以是和View有关系的任意的Object
    //            return super.instantiateItem(container, position);
                return view;
            }
    
            /**
             * 判断某个page和object的关系
             * object 是 instantiateItem的返回值
             */
            @Override
            public boolean isViewFromObject(View view, Object object) {
    //            if(view ==object){
    //                return true;
    //            }else{
    //                return false;
    //            }
                return view ==object;
                
            }
    
            /**
             * 销毁指定位置上的View或者object
             */
            @Override
            public void destroyItem(ViewGroup container, int position, Object object) {
                System.out.println("destroyItem=="+position);
                container.removeView((View) object);
    //            super.destroyItem(container, position, object);
            }
    
        }



        6_解决运行报错
          选中项目--->右键--->Java Build Path --->
          order export--->勾选android-support-v4.jar--->千万不要忘了clean
          


      2_广告条基本功能

         1_根据不同图片显示不同描述信息

    viewpager.setOnPageChangeListener(new OnPageChangeListener() {
                
                /**
                 * 当页面被选择了回调
                 * position 当前被显示的页面的位置:从0开始
                 */
                @Override
                public void onPageSelected(int position) {
                                            tv_image_desc.setText(imageDescriptions[position]);    
            }
                /**
                 * 当页面滑动了调用该方法
                 */
                @Override
                public void onPageScrolled(int position, float positionOffset,
                        int positionOffsetPixels) {            
                }
                /**
                 * 当页面状态发送变化的调用防方法
                 * 静止--滑动
                 * 滑动-静止
                 * 
                 */
                @Override
                public void onPageScrollStateChanged(int state) {
                }
            });

        2.用shape资源定义点和背景
          创建drawable目录里面创建文件
          point_normal.xml

    <?xml version="1.0" encoding="utf-8"?>
    <shape xmlns:android="http://schemas.android.com/apk/res/android"
        android:shape="oval" >
        <size   android:height="5dip"  android:width="5dip" />
        <solid android:color="#55000000"/>
    </shape>

          point_focused.xml 

    <?xml version="1.0" encoding="utf-8"?>
    <shape xmlns:android="http://schemas.android.com/apk/res/android"
        android:shape="oval" >
        <size   android:height="5dip"  android:width="5dip" />
        <solid android:color="#aaffffff"/>
    </shape>

          point_selsetor.xml

    <?xml version="1.0" encoding="utf-8"?>
    <selector xmlns:android="http://schemas.android.com/apk/res/android">
        <item android:state_enabled="true"  android:drawable="@drawable/point_focused" /> 
    <item android:state_enabled="false"  android:drawable="@drawable/point_normal" /> 
    </selector>

        3.代码里面添加指示点

    for(int i=0;i<imageIds.length;i++){
                ImageView imageView = new ImageView(this);
                imageView.setBackgroundResource(imageIds[i]);
                imageViews.add(imageView);
                
                //添加指示点
                ImageView point = new ImageView(this);
                point.setBackgroundResource(R.drawable.point_selsetor);
                ll_point_group.addView(point);
                
                //默认情况下,第一个小点enable为true
                if(i ==0){
                    point.setEnabled(true);
                }else{
                    point.setEnabled(false);
                }
    
    }


        4_设置改变指示点的状态
          如字体加粗部分

    /**
         * 上次的位置
         */
    private int lastPointIndex;
    
    viewpager.setOnPageChangeListener(new OnPageChangeListener() {
                
                /**
                 * 当页面被选择了回调
                 * position 当前被显示的页面的位置:从0开始
                 */
                @Override
                public void onPageSelected(int position) {
                    System.out.println("onPageSelected="+position);
                    tv_image_desc.setText(imageDescriptions[position]);
                    
                    //设置指示点的状态 enable 的状态为true或者为false;
                    ll_point_group.getChildAt(position).setEnabled(true);
                    
                    ll_point_group.getChildAt(lastPointIndex).setEnabled(false);
                    lastPointIndex = position;
                }
                /**
                 * 当页面滑动了调用该方法
                 */
                @Override
                public void onPageScrolled(int position, float positionOffset,
                        int positionOffsetPixels) {
                    
                }
                /**
                 * 但页面状态发送变化的调用防方法
                 * 静止--滑动
                 * 滑动-静止
                 * 
                 */
                @Override
                public void onPageScrollStateChanged(int state) {
                    System.out.println("onPageScrollStateChanged===state=="+state);
                }
            });

          5.设置指示点的间距
            如字体加粗部分

    for(int i=0;i<imageIds.length;i++){
                ImageView imageView = new ImageView(this);
                imageView.setBackgroundResource(imageIds[i]);
                imageViews.add(imageView);
                
                //添加指示点
                ImageView point = new ImageView(this);
                
                LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT, -2);
                params.leftMargin = 15;
                point.setLayoutParams(params);
                
                point.setBackgroundResource(R.drawable.point_selsetor);
                ll_point_group.addView(point);
                
                //默认情况下,第一个小点enable为true
                if(i ==0){
                    point.setEnabled(true);
                }else{
                    point.setEnabled(false);
                }
            }

            注意导入包的时候,当前控件放入什么布局就导入谁的LayoutParams的。

          6_设置可以循环滑动

    viewpager.setOnPageChangeListener(new OnPageChangeListener() {
                
                /**
                 * 当页面被选择了回调
                 * position 当前被显示的页面的位置:从0开始
                 */
                @Override
                public void onPageSelected(int position) {
                    int  myIndex = position % imageViews.size();
                    System.out.println("onPageSelected="+position);
                    tv_image_desc.setText(imageDescriptions[myIndex]);
                    
                    //设置指示点的状态 enable 的状态为true或者为false;
                    ll_point_group.getChildAt(myIndex).setEnabled(true);
                    
                    ll_point_group.getChildAt(lastPointIndex).setEnabled(false);
                    lastPointIndex = myIndex;
                }
                ..............................
            });
        }
    
        private class MyPagerAdapter extends PagerAdapter {
    
            @Override
            public int getCount() {
                //得到数据的总数
    //            return imageViews.size();
                return Integer.MAX_VALUE;
            }
    
            /**
             * 给ViewPager添加指定的View
             * container 是ViewPage,他是一个容器
             * position 要实例化的view的位置
             */
            @Override
            public Object instantiateItem(ViewGroup container, int position) {
    //            System.out.println("instantiateItem=="+position);
                //实例化View
                View view = imageViews.get(position%imageViews.size());
                container.addView(view);
                //返回值,不一定要是View对象,也可以是和View有关系的任意object
    //            return super.instantiateItem(container, position);
                return view;
            }
            ......................
        }



          7_解决左滑没有效果问题

    //要求刚好是imageViews.size()的整数倍
    int item = Integer.MAX_VALUE/2-Integer.MAX_VALUE/2%imageViews.size();
    //让ViewPager跳转到指定的位置,应该保证是imageView.size()的整数倍
    viewpager.setCurrentItem(item );
    //11 和 101


      3_广告条自动翻页(自动循环播放)

         实现方式有多种方案:
          1.定时器 timer + Handler
          2.while true 循环 sleep  + Handler;
          3.ClockManger + Handler ;
          4.Handler
          我们采用常用的方式Handler

    /**
         * 是否自定滑动运行中
         */
        private boolean isRunning = false;
        
        private Handler handler = new Handler(){
            public void handleMessage(android.os.Message msg) {
                viewpager.setCurrentItem(viewpager.getCurrentItem()+1);
                if(isRunning){
                    handler.sendEmptyMessageDelayed(0, 4000);
                }
                
            };
        };
    
    
    在onCreate中写上
    isRunning = true;
    handler.sendEmptyMessageDelayed(0, 2000);

    创建onDestroy写上
    //页面销毁停止自动播放动画
    isRunning = false;

    4.下拉框

        下拉框效果:
        在editText的右边放置一个小箭头的图片,点击图片,在editText的下方弹出一个popupWindow,并对popupWindow进行一些设置即得到想要的效果。
        

      1_新建一个工程:

          下拉框,把需要的图片拷贝到工程中,包名:com.bokeyuan.popupwindow

      2_写布局文件

    代码如下

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >
    
        <EditText
            android:id="@+id/et_input"
            android:paddingRight="40dip"
            android:layout_marginTop="20dip"
            android:layout_centerHorizontal="true"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/hello_world" />
        <ImageView 
            android:id="@+id/dowan_arrow"
            android:layout_alignRight="@id/et_input"
            android:layout_alignTop="@id/et_input"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="5dip"
            android:layout_marginRight="5dip"
            android:background="@drawable/down_arrow"/>
    
    </RelativeLayout>

      3_实例化控件并准备数据

    public class MainActivity extends Activity {
        private EditText et_input;
        private ImageView downArrow;
        
        /**
         * 装数据的集合
         */
        private ArrayList<String> msgList;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            et_input = (EditText) findViewById(R.id.et_input);
            downArrow = (ImageView) findViewById(R.id.dowan_arrow);
            //准备数据
            msgList = new ArrayList<String>();
            for(int i=0;i<30;i++){
                msgList.add("aaaaaaaaaa"+i);
            }
        }
    }

        

      4_设置向下箭头点击事件并实例化popupwindow&TODO简介 

    downArrow.setOnClickListener(this);
    
    //浮悬的窗体
        private PopupWindow  popupWindow;
        @Override
        public void onClick(View v) {
            switch (v.getId()) {
            case R.id.dowan_arrow:
                if(popupWindow == null){
                    popupWindow = new PopupWindow(this);
                    //设置高和宽
                    popupWindow.setWidth(et_input.getWidth());
                    popupWindow.setHeight(200);
                    //设置窗体的内容
                    //TODO ListView 还没有初始化
                    popupWindow.setContentView(listView);
                    
                }
                popupWindow.showAsDropDown(et_input, 0, 0);
                
                break;
    
            default:
                break;
            }

        

      5_实例化ListView并且设置适配器


          在onCreate方法中实例化ListView

    //实例化ListView
    listView = new ListView(this);
    listView.setAdapter(new MyAdapter());

          自定义适配器

    class MyAdapter extends BaseAdapter{
    
            @Override
            public int getCount() {
                return msgList.size();
            }
            @Override
            public View getView(final int position, View convertView, ViewGroup parent) {
                View view;
                ViewHolder holder;
                if(convertView != null){
                    view = convertView;
                    holder = (ViewHolder) view.getTag();
                }else{
                    view =  View.inflate(MainActivity.this, R.layout.list_popupwindow_item, null);
                    holder = new ViewHolder();
                    holder.iv_user = (ImageView) view.findViewById(R.id.iv_user);
                    holder.tv_tilte = (TextView) view.findViewById(R.id.tv_tilte);
                    holder.iv_delete = (ImageView) view.findViewById(R.id.iv_delete);
                    view.setTag(holder);
                }
                holder.tv_tilte.setText(msgList.get(position));
                holder.iv_delete.setOnClickListener(new OnClickListener() {
                    
                    @Override
                    public void onClick(View v) {
                        //1.把点击的条在列表中移除
                        msgList.remove(position);
                        //2.更新数据
                        notifyDataSetChanged();
                    }
                });            
                return view;
            }
            @Override
            public Object getItem(int position) {
                return null;
            }
    
            @Override
            public long getItemId(int position) {
                // TODO Auto-generated method stub
                return 0;
            }
            
        }
        
        class ViewHolder{
            ImageView iv_user;
            TextView tv_tilte;
            ImageView iv_delete;
        }

          每条布局文件代码list_popupwindow_item.xml

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="fill_parent"
        android:layout_height="55dip"
        android:gravity="center_vertical"
        android:padding="15dip" >
    
        <ImageView
            android:id="@+id/iv_user"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@drawable/user"
            android:padding="5dp" />
    
        <TextView
            android:id="@+id/tv_tilte"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerHorizontal="true"
            android:text="aaaaaaaaa1" />
    
        <ImageView
            android:id="@+id/iv_delete"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentRight="true"
            android:background="@drawable/delete"
            android:padding="5dp" />
    
    </RelativeLayout>

          演示运行看看效果

      6_ListView在低版本2.3的适配并且解决各个问题

          设置输入框的宽为200dip

    <EditText
            android:id="@+id/et_input"
            android:paddingRight="40dip"
            android:layout_marginTop="20dip"
            android:layout_centerHorizontal="true"
            android:layout_width="200dip"
            android:layout_height="wrap_content"
            android:text="@string/hello_world" />

          解决按下变白的问题:    

    listView = new ListView(this);
    listView.setBackgroundResource(R.drawable.listview_background);
    listView.setAdapter(new MyAdapter());

          解决点击popupwindow外部,无法消掉问题

    popupWindow.setOutsideTouchable(true);

          设置选择某一条,并且显示在输入框中

    listView.setOnItemClickListener(new OnItemClickListener() {
    
                @Override
                public void onItemClick(AdapterView<?> parent, View view,
                        int position, long id) {
                    et_input.setText(msgList.get(position));
    
                }
            });

          注意需要设置popupwindow的焦点才起作用

    popupWindow.setFocusable(true);

          在setOnItemClickListener方法中消掉对话框

    popupWindow.dismiss();
    
    @Override
        public void onClick(View v) {
            switch (v.getId()) {
            case R.id.iv_down_arrow://点击向下箭头
                if(window == null){
                    window = new PopupWindow(this);
    
    //                window.setBackgroundDrawable(new ColorDrawable(color.transparent));
                    window.setWidth(et_input.getWidth());
                    window.setHeight(200);
    
                    //TODO 设置popupWindow的内容
                    window.setContentView(contentView);
    
    //                window.setOutsideTouchable(true);
                    //不一定要背景,主要是setFocusable要先执行,showAsDropDown后执行
                    window.setFocusable(true);
                }
                window.showAsDropDown(et_input, 0, 0);
                break;
    
            default:
                break;
            }
    
        }

    5.自定义开关按钮

      1_自定义点击开关按钮

          继承已有View实现自定义View

          通过对android原生控件的研究,可以发现android中的控件都是继承view类,如textView、ImageView等,通过重写相关的方法来实现新的效果,通过这个我们得到两点:
          我们可以在已有控件的基础上,通过重写相关方法来实现我们的需求。
          继承view类或viewgroup类,来创建我们所需要的控件。一般来讲,通过继承已有的控件,来自定义控件要简单一点。
          
          

          

        1_创建工程:

          开关按钮,包名:com.itheima.togglebutton,并把图片拷贝到工程中

        2_自定义类MyToggleButton继承自View

    实现三个构造方法
    /**
     * 自定按钮
     * @author afu
     */
    public class MyToggleButton extends View {
    
        // 增加一个默认显示样式时候使用
        public MyToggleButton(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
        }
    
        // 在布局文件中声明view的时候,该方法有系统调用
        public MyToggleButton(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
    
        // 在代码中new实例化时调用
        public MyToggleButton(Context context) {
            super(context);
        }
    
    }

    在布局文件中使用

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >
    
    <com.itheima.togglebutton.MyToggleButton
      android:layout_centerHorizontal="true"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
    
    </RelativeLayout>

        3_一个View从创建到显示屏幕的步骤

          1.执行view构造方法,创建对象

          2.测量view大小
                  onMeasure(int,int);来完成测量动作
            3.指定view的位置,子View只有建议权,父View才有决定权;
               onLayout(boolean,int,int,int ,int);
               这个方法一般用不着,如果自定义继承ViewGoup才用到
            4.绘制view的内容
              onDraw(canvas);

        4_画个矩形背景和圆形

    package com.bokeyuan.togglebutton;
    
    import android.content.Context;
    import android.graphics.Canvas;
    import android.graphics.Color;
    import android.graphics.Paint;
    import android.util.AttributeSet;
    import android.view.View;
    
    /**
     * 自定按钮
     * @author m
     */
    public class MyToggleButton extends View {
        
        /**
         * 一个View从创建到显示屏幕上的主要步骤:
         * 1.执行view构造方法,创建对象
         * 2.测量view大小
         *  onMeasure(int,int);来完成测量动作
         * 3.指定view的位置,子View只有建议权,父View才有决定权;
         * onLayout(boolean,int,int,int ,int);
         * 这个方法一般用不着,如果自定义ViewGoup才用到
         * 4.绘制view的内容
         * onDraw(canvas);
         * 
         */
        
        private Paint paint;
    
        /**
         * 测量
         */
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    //        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            //设置当前view的测量大小
            setMeasuredDimension(100, 100);
        }
        /**
         * 绘制
         */
        @Override
        protected void onDraw(Canvas canvas) {
    //        super.onDraw(canvas);
            //绘制颜色,可以理解成背景颜色
            canvas.drawColor(Color.RED);
            //绘制圆形
            canvas.drawCircle(50, 50, 20, paint);
        }
        
        private void init(Context context) {
            paint = new Paint();
            paint.setColor(Color.GREEN);
            //设置抗锯齿,让边缘圆滑,一般都会设置
            paint.setAntiAlias(true);
            
        }
        // 增加一个默认显示样式时候使用
        public MyToggleButton(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
            init(context);
        }
    
        // 在布局文件中声明view的时候,该方法有系统调用
        public MyToggleButton(Context context, AttributeSet attrs) {
            super(context, attrs);
            init(context);
        }
    
        // 在代码中new实例化时调用
        public MyToggleButton(Context context) {
            super(context);
            init(context);
        }
    }

        5_画按钮背景

    package com.bokeyuan.togglebutton;
    
    import android.content.Context;
    import android.graphics.Bitmap;
    import android.graphics.BitmapFactory;
    import android.graphics.Canvas;
    import android.graphics.Color;
    import android.graphics.Paint;
    import android.util.AttributeSet;
    import android.view.View;
    import android.widget.ImageView;
    
    /**
     * 自定按钮
     * @author m
     */
    public class MyToggleButton extends View {
        
        /**
         * 一个View从创建到显示屏幕上的主要步骤:
         * 1.执行view构造方法,创建对象
         * 2.测量view大小
         *  onMeasure(int,int);来完成测量动作
         * 3.指定view的位置,子View只有建议权,父View才有决定权;
         * onLayout(boolean,int,int,int ,int);
         * 这个方法一般用不着,如果自定义ViewGoup才用到
         * 4.绘制view的内容
         * onDraw(canvas);
         * 
         */
        
        private Paint paint;
        private Bitmap backGroundBitmap;
        private Bitmap slideBitmap;
        private Context context;
    
        /**
         * 测量
         */
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    //        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            //设置当前view的测量大小
            setMeasuredDimension(backGroundBitmap.getWidth(), backGroundBitmap.getHeight());
        }
        /**
         * 绘制
         */
        @Override
        protected void onDraw(Canvas canvas) {
    //        super.onDraw(canvas);
            //绘制颜色,可以理解成背景颜色
    //        canvas.drawColor(Color.RED);
            //绘制圆形
    //        canvas.drawCircle(50, 50, 20, paint);
            canvas.drawBitmap(backGroundBitmap, 0, 0, paint);
        }
        
        private void init(Context context) {
            this.context = context;
            paint = new Paint();
            paint.setColor(Color.GREEN);
            //设置抗锯齿,让边缘圆滑,一般都会设置
            paint.setAntiAlias(true);
            
            //初始化图片-从资源文件中解析成Bitmap对象
            slideBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.slide_button);
            backGroundBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.switch_background);
            
        }
        // 增加一个默认显示样式时候使用
        public MyToggleButton(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
            init(context);
        }
    
        // 在布局文件中声明view的时候,该方法有系统调用
        public MyToggleButton(Context context, AttributeSet attrs) {
            super(context, attrs);
            init(context);
        }
    
        // 在代码中new实例化时调用
        public MyToggleButton(Context context) {
            super(context);
            init(context);
        }
        
    
    }

        6_画滑动按钮

    canvas.drawBitmap(slideBitmap, 45, 0, paint);

         分别设置0和30运行看看效果

        7_点击时改变按钮状态

    package com.bokeyuan.togglebutton;
    
    import android.content.Context;
    import android.graphics.Bitmap;
    import android.graphics.BitmapFactory;
    import android.graphics.Canvas;
    import android.graphics.Color;
    import android.graphics.Paint;
    import android.util.AttributeSet;
    import android.view.View;
    import android.widget.ImageView;
    
    /**
     * 自定按钮
     * @author m
     */
    public class MyToggleButton extends View implements  View.OnClickListener {
        
        /**
         * 一个View从创建到显示屏幕上的主要步骤:
         * 1.执行view构造方法,创建对象
         * 2.测量view大小
         *  onMeasure(int,int);来完成测量动作
         * 3.指定view的位置,子View只有建议权,父View才有决定权;
         * onLayout(boolean,int,int,int ,int);
         * 这个方法一般用不着,如果自定义ViewGoup才用到
         * 4.绘制view的内容
         * onDraw(canvas);
         * 
         */
        
        private Paint paint;
        private Bitmap backGroundBitmap;
        private Bitmap slideBitmap;
        private Context context;
        /**
         * 距离左边的距离
         */
        private float slideLeft;
        /**
         * 判断当前开关状态
         * true为开
         * false为关
         */
        private boolean curStata = false;
    
        /**
         * 测量
         */
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    //        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            //设置当前view的测量大小
            setMeasuredDimension(backGroundBitmap.getWidth(), backGroundBitmap.getHeight());
        }
        /**
         * 绘制
         */
        @Override
        protected void onDraw(Canvas canvas) {
    //        super.onDraw(canvas);
            //绘制颜色,可以理解成背景颜色
    //        canvas.drawColor(Color.RED);
            //绘制圆形
    //        canvas.drawCircle(50, 50, 20, paint);
            canvas.drawBitmap(backGroundBitmap, 0, 0, paint);
            //绘制滑动按钮
            canvas.drawBitmap(slideBitmap, slideLeft, 0, paint);
        }
        
        private void init(Context context) {
            this.context = context;
            paint = new Paint();
            paint.setColor(Color.GREEN);
            //设置抗锯齿,让边缘圆滑,一般都会设置
            paint.setAntiAlias(true);
            
            //初始化图片-从资源文件中解析成Bitmap对象
            slideBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.slide_button);-
            backGroundBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.switch_background);
            
            setOnClickListener( MyToggleButton.this);
            
        }
        // 增加一个默认显示样式时候使用
        public MyToggleButton(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
            init(context);
        }
    
        // 在布局文件中声明view的时候,该方法有系统调用
        public MyToggleButton(Context context, AttributeSet attrs) {
            super(context, attrs);
            init(context);
        }
    
        // 在代码中new实例化时调用
        public MyToggleButton(Context context) {
            super(context);
            init(context);
        }
        
        @Override
        public void onClick(View v) {
            curStata = !curStata;
            flushState();
            
        }
        /**
         * 刷新状态
         */
        private void flushState() {
            //设置距离左边的距离
            if(curStata){
                slideLeft = backGroundBitmap.getWidth()-slideBitmap.getWidth();
            }else{
                slideLeft = 0;
            }
            
            /**
             * 刷新View,会导致当前View的onDraw方法执行
             */
            invalidate();
        }
    }

      2_自定义滑动开关按钮

        1_实现滑动效果

              实现思想,参照手机卫士中的拖动的原理

     /**
         * 第一次按下的x坐标
         */
        int startX = 0;
        
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            super.onTouchEvent(event);
            switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN://按下
                //1.记录第一次按下坐标
                startX = (int) event.getRawX();
    
                break;
            case MotionEvent.ACTION_MOVE://滑动
                //2.来到新的坐标
                int newX = (int) event.getRawX();
                //3.计算偏移量
                int dX = newX - startX;
                slideLeft += dX;
                //4.更新UI-onDraw方法即可--invalidate();
                invalidate();
                //5.重新记录坐标
                startX = (int) event.getRawX();
                break;
            case MotionEvent.ACTION_UP://离开
    
                break;
    
            default:
                break;
            }
            
            return true;
        }

        2_取消点击事件,屏蔽非法滑动

    public class MyToggleButton extends View implements OnClickListener {
    
        private Paint paint;
    
        /**
         * 一个View从创建到显示到屏幕过程中的步骤: 1.执行View的构造方法,实例化;通常在构造方法里面加载资源 2.测量view对象
         * onMeasure(int,int) 3.指定View的位置 - 一般的View用不到,自定义包括其他View进来这样的控才用到
         * onLayout(boolean,int,int,int,int) 4.绘制View对象 onDraw(canvas)
         * 
         */
    
        private Bitmap backgroundBitmap;
        private Bitmap slideBitmap;
    
        /**
         * 滑动图片,距离左边的距离
         */
        private float slideLeft;
        /**
         * 按钮的状态 false为关闭 true为开
         */
        private boolean curState = false;
    
        // 测量
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            // super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            // 设置测量值
            setMeasuredDimension(backgroundBitmap.getWidth(),
                    backgroundBitmap.getHeight());
        }
    
        // 绘制
        @Override
        protected void onDraw(Canvas canvas) {
            // super.onDraw(canvas);
            // canvas.drawColor(Color.GREEN);
            // canvas.drawCircle(50, 50, 20, paint);
            canvas.drawBitmap(backgroundBitmap, 0, 0, paint);
            canvas.drawBitmap(slideBitmap, slideLeft, 0, paint);
        }
        /**
         * 第一次按下的x坐标
         */
        int startX = 0;
        int maxLeft;
        
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            super.onTouchEvent(event);
            switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN://按下
                //1.记录第一次按下坐标
                startX = (int) event.getRawX();
    
                break;
            case MotionEvent.ACTION_MOVE://滑动
                //2.来到新的坐标
                int newX = (int) event.getRawX();
                //3.计算偏移量
                int dX = newX - startX;
                slideLeft += dX;
                //4.更新UI-onDraw方法即可--invalidate();
                flushView();
                //5.重新记录坐标
                startX = (int) event.getRawX();
                break;
            case MotionEvent.ACTION_UP://离开
    
                break;
    
            default:
                break;
            }
            
            return true;
        }
    
      // 刷新View的状态,并且纠正非法滑动
        private void flushView() {
            if(slideLeft < 0){
                slideLeft = 0;
            }
            
            if(slideLeft > maxLeft){
                slideLeft = maxLeft;
            }
            //屏蔽非法滑动
            invalidate();
        }
    
        private void init(Context context) {
            paint = new Paint();
            paint.setColor(Color.RED);
            // 设置抗锯齿-使其变得光滑
            paint.setAntiAlias(true);
            // 加载资源图片
            backgroundBitmap = BitmapFactory.decodeResource(getResources(),
                    R.drawable.switch_background);
            slideBitmap = BitmapFactory.decodeResource(getResources(),
                    R.drawable.slide_button);
            
            //滑动图片距离左边的距离
            maxLeft = backgroundBitmap.getWidth()-slideBitmap.getWidth();
    
            // 设置点击事件
    //        setOnClickListener(this);
        }
    
        // 一般会在代码中实例化
        public MyToggleButton(Context context) {
            super(context);
            init(context);
        }
    
        // 带有两个参数的构造方法,在布局文件中使用的时候,就会回调
        public MyToggleButton(Context context, AttributeSet attrs) {
            super(context, attrs);
            init(context);
        }
    
        // 我们需要设置默认的样式风格的时候
        public MyToggleButton(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
            init(context);
        }
    
        @Override
        public void onClick(View v) {
            curState = !curState;
    
            flushState();
        }
    
        // 刷新View的状态
        private void flushState() {
            if (curState) {
                
                slideLeft = maxLeft;
            } else {
                slideLeft = 0;
            }
            flushView();
        }
    }

        3_处理滑动到一小半时时不好看的问题

          先画图分析

          
          代码如下:

    @Override
        public boolean onTouchEvent(MotionEvent event) {
            super.onTouchEvent(event);
            switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN://按下
                //1.记录第一次按下坐标
                startX = (int) event.getRawX();
    
                break;
            case MotionEvent.ACTION_MOVE://滑动
                //2.来到新的坐标
                int newX = (int) event.getRawX();
                //3.计算偏移量
                int dX = newX - startX;
                slideLeft += dX;
                //4.更新UI-onDraw方法即可--invalidate();
                flushView();
                //5.重新记录坐标
                startX = (int) event.getRawX();
                break;
            case MotionEvent.ACTION_UP://离开
                /**
                 *  当UP事件发生的时候,由按钮的左边距离(btn_left)确定View的状态;
                    当btn_left >= maxLeft/2 设置为开状态
                    当btn_left < maxLeft/2  设置为 关闭状态
                 */
                
                if(slideLeft >= maxLeft/2){
                    curState = true;
                }else{
                    curState = false;
                }
                flushState();
                break;
    
            default:
                break;
            }
            
            return true;
        }

          恢复点击事件
          演示会有bug

      3_ 解决点击事件和滑动事件导致的bug

    public class MyToggleButton extends View implements OnClickListener {
    
        private Paint paint;
    
        /**
         * 一个View从创建到显示到屏幕过程中的步骤: 1.执行View的构造方法,实例化;通常在构造方法里面加载资源 2.测量view对象
         * onMeasure(int,int) 3.指定View的位置 - 一般的View用不到,自定义包括其他View进来这样的控才用到
         * onLayout(boolean,int,int,int,int) 4.绘制View对象 onDraw(canvas)
         * 
         */
    
        private Bitmap backgroundBitmap;
        private Bitmap slideBitmap;
    
        /**
         * 滑动图片,距离左边的距离
         */
        private float slideLeft;
        /**
         * 按钮的状态 false为关闭 true为开
         */
        private boolean curState = false;
    
        // 测量
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            // super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            // 设置测量值
            setMeasuredDimension(backgroundBitmap.getWidth(),
                    backgroundBitmap.getHeight());
        }
    
        // 绘制
        @Override
        protected void onDraw(Canvas canvas) {
            // super.onDraw(canvas);
            // canvas.drawColor(Color.GREEN);
            // canvas.drawCircle(50, 50, 20, paint);
            canvas.drawBitmap(backgroundBitmap, 0, 0, paint);
            canvas.drawBitmap(slideBitmap, slideLeft, 0, paint);
        }
        /**
         * 第一次按下的x坐标
         */
        int startX = 0;
        /**
         * 最初的历史位置
         */
        int lastX = 0;
        /**
         * 滑动按钮距离左边的最大距离
         */
        int maxLeft;
        /**
         * 点击事件是否可用
         * true 可用
         * false 不可用
         */
        boolean isClickEnable = true;
        
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            super.onTouchEvent(event);
            switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN://按下
                //1.记录第一次按下坐标
                lastX = startX = (int) event.getRawX();
                isClickEnable = true;
                break;
            case MotionEvent.ACTION_MOVE://滑动
                //2.来到新的坐标
                int newX = (int) event.getRawX();
                //3.计算偏移量
                int dX = newX - startX;
                slideLeft += dX;
                //4.更新UI-onDraw方法即可--invalidate();
                if(Math.abs(event.getRawX()-lastX)>5){
                    isClickEnable = false;
                }
                flushView();
                //5.重新记录坐标
                startX = (int) event.getRawX();
                break;
            case MotionEvent.ACTION_UP://离开
                
                if(!isClickEnable){
                    /**
                     *  当UP事件发生的时候,由按钮的左边距离(btn_left)确定View的状态;
                        当btn_left >= maxLeft/2 设置为开状态
                        当btn_left < maxLeft/2  设置为 关闭状态
                     */
                    
                    if(slideLeft >= maxLeft/2){
                        curState = true;
                    }else{
                        curState = false;
                    }
                    flushState();
                }
                
                break;
    
            default:
                break;
            }
            
            return true;
        }
    
      // 刷新View的状态,并且纠正非法滑动
        private void flushView() {
            if(slideLeft < 0){
                slideLeft = 0;
            }
            
            if(slideLeft > maxLeft){
                slideLeft = maxLeft;
            }
            //屏蔽非法滑动
            invalidate();
        }
    
        private void init(Context context) {
            paint = new Paint();
            paint.setColor(Color.RED);
            // 设置抗锯齿-使其变得光滑
            paint.setAntiAlias(true);
            // 加载资源图片
            backgroundBitmap = BitmapFactory.decodeResource(getResources(),
                    R.drawable.switch_background);
            slideBitmap = BitmapFactory.decodeResource(getResources(),
                    R.drawable.slide_button);
            
            //滑动图片距离左边的距离
            maxLeft = backgroundBitmap.getWidth()-slideBitmap.getWidth();
    
            // 设置点击事件
            setOnClickListener(this);
        }
    
        // 一般会在代码中实例化
        public MyToggleButton(Context context) {
            super(context);
            init(context);
        }
    
        // 带有两个参数的构造方法,在布局文件中使用的时候,就会回调
        public MyToggleButton(Context context, AttributeSet attrs) {
            super(context, attrs);
            init(context);
        }
    
        // 我们需要设置默认的样式风格的时候
        public MyToggleButton(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
            init(context);
        }
    
        @Override
        public void onClick(View v) {
            if(isClickEnable){
                curState = !curState;
    
                flushState();
            }
            
        }
    
        // 刷新View的状态
        private void flushState() {
            if (curState) {
                
                slideLeft = maxLeft;
            } else {
                slideLeft = 0;
            }
            flushView();
        }
    }
  • 相关阅读:
    Angular
    linux mysql 5.7.25 安裝
    J2CACHE 两级缓存框架
    MYSQL 事务测试
    安装配置ftp服务器
    使用docker 安装 GITLIB
    Elastic serarch 安装
    centos firewalld 基本操作【转】
    KAFKA 监控管理界面 KAFKA EAGLE 安装
    redis 的一主二从三哨兵模式
  • 原文地址:https://www.cnblogs.com/McCa/p/5316347.html
Copyright © 2020-2023  润新知