目录:
1.自定义控件概述
01_什么是自定义控件
Android系统中,继承Android系统自带的View或者ViewGroup控件或者系统自带的控件,并在这基础上增加或者重新组合成我们想要的效果。
02_为什么用自定义控件
系统控件无法满足需求时,需要自定义控件。
-
- 系统的控件在不同手机长得不一样,我们希望在不同手机实现相同的效果;
- 有些手机上的控件长得不好看,希望好看一些。
- 系统控件的功能有限,需要在基础上增加功能。
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:\android\adt-bundle-windows-x86_64-20130219\sdk\extras\android\support\v4\src\java
点击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(); for(int i=0;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){ 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){ 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 ArrayListmsgList; @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 (); 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(); } }