Android笔记——Android自定义控件

时间:2023-03-08 22:28:44

目录:

Android笔记——Android自定义控件

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.

  Android笔记——Android自定义控件

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

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

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

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

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

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

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

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

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

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

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

  Android笔记——Android自定义控件                            Android笔记——Android自定义控件
  知识链接:
    android WheelView组件(滑轮组件)的使用 : http://www.myexception.cn/android/1236819.html

  Android笔记——Android自定义控件  

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

2.优酷效果

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

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

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

    旋转原理画图分析:    

  Android笔记——Android自定义控件

    旋转工具类代码:  

/**
* @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描述:一二三级菜单全部隐藏状态,再点击手机菜单位置,二三级菜单会显示

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

    解决:用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.广告条和首页推荐

  Android笔记——Android自定义控件

  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,删除后会报错,不要理会。看下面

            Android笔记——Android自定义控件

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

          3.关联源代码
            目录:C:\android\adt-bundle-windows-x86_64-20130219\sdk\extras\android\support\v4\src\java
            点击ViewPager类,出现图标;

            Android笔记——Android自定义控件

            大家对于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的原理
            Android笔记——Android自定义控件
          能显示很多页面,者些页面可以是图片也可以是布局文件。

      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
      Android笔记——Android自定义控件

  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进行一些设置即得到想要的效果。
    Android笔记——Android自定义控件

  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类,来创建我们所需要的控件。一般来讲,通过继承已有的控件,来自定义控件要简单一点。
      Android笔记——Android自定义控件
      

      Android笔记——Android自定义控件

    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_处理滑动到一小半时时不好看的问题

      先画图分析

      Android笔记——Android自定义控件
      代码如下:

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