zoukankan      html  css  js  c++  java
  • 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();
        }
    }
  • 相关阅读:
    (二分查找 拓展) leetcode 69. Sqrt(x)
    (二分查找 拓展) leetcode 162. Find Peak Element && lintcode 75. Find Peak Element
    (链表) lintcode 219. Insert Node in Sorted Linked List
    (二分查找 拓展) leetcode 34. Find First and Last Position of Element in Sorted Array && lintcode 61. Search for a Range
    (最短路 Floyd) P2910 [USACO08OPEN]寻宝之路Clear And Present Danger 洛谷
    (字符串 数组 递归 双指针) leetcode 344. Reverse String
    (二叉树 DFS 递归) leetcode 112. Path Sum
    (二叉树 DFS 递归) leetcode 101. Symmetric Tree
    (二叉树 递归) leetcode 144. Binary Tree Preorder Traversal
    (二叉树 递归 DFS) leetcode 100. Same Tree
  • 原文地址:https://www.cnblogs.com/McCa/p/5316347.html
Copyright © 2011-2022 走看看