目录:

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

Android笔记——Android自定义控件的更多相关文章

  1. Android笔记——Android中数据的存储方式(二)

    我们在实际开发中,有的时候需要储存或者备份比较复杂的数据.这些数据的特点是,内容多.结构大,比如短信备份等.我们知道SharedPreferences和Files(文本文件)储存这种数据会非常的没有效 ...

  2. Android笔记——Android中数据的存储方式(三)

    Android系统集成了一个轻量级的数据库:SQLite,所以Android对数据库的支持很好,每个应用都可以方便的使用它.SQLite作为一个嵌入式的数据库引擎,专门适用于资源有限的设备上适量数据存 ...

  3. Android笔记——Android中visibility属性VISIBLE、INVISIBLE、GONE的区别

    在Android开发中,大部分控件都有visibility这个属性,其属性有3个分别为"visible "."invisible"."gone&quo ...

  4. Android笔记——Android五大布局

    一.五大布局 Android的界面是有布局和组件协同完成的,布局好比是建筑里的框架,而组件则相当于建筑里的砖瓦.组件按照布局的要求依次排列,就组成了用户所看见的界面.Android的五大布局分别是Li ...

  5. Android笔记——Android框架

    本篇将站在顶级的高度--架构,来看android.我开篇就说了,这个系列适合0基础的人且我也是从0开始按照这个步骤来 学的,谈架构是不是有点螳臂挡车,自不量力呢?我觉得其实不然,如果一开始就对整个an ...

  6. Android笔记——Android内部类

    Java语言允许在类中再定义类,这种在其它类内部定义的类就叫内部类.内部类又分为:常规内部类.局部内部类.匿名内部类和静态嵌套类四种.我们内部类的知识在Android手机开发中经常用到. 一.常规内部 ...

  7. Android笔记——Android中数据的存储方式(一)

    Android中数据的存储方式 对于开发平台来讲,如果对数据的存储有良好的支持,那么对应用程序的开发将会有很大的促进作用. 总体的来讲,数据存储方式有三种:一个是文件,一个是数据库,另一个则是网络.其 ...

  8. Android笔记--自定义控件仿遥控器的圆形上下左右OK圆盘按钮

    原文:Android笔记--自定义控件仿遥控器的圆形上下左右OK圆盘按钮 上面就是几张预览图!代码在最底下 主要就两个步骤,画图.监听点击 1.整个控件基本上是一步步画出来的,重写onDraw方法开始 ...

  9. Android中的自定义控件(二)

    案例四: 自定义开关       功能介绍:本案例实现的功能是创建一个自定义的开关,可以自行决定开关的背景.当滑动开关时,开关的滑块可跟随手指移动.当手指松开后,滑块根据开关的状态,滑到最右边或者滑到 ...

随机推荐

  1. 让Unity NavMesh为我所用

    Unity里面整合了一个NavMesh功能,虽然让人又爱又恨. 但当你在其他地方需要这个NavMesh的数据时,就更让人欲罢不能了. 比如说服务器需要Unity的NavMesh数据时. 比如说你想将U ...

  2. Marmoset Toolbag中的角色布光技巧 by Joe”EarthQuake”Wilson

    Sagat by Tim “spacemonkey” Appleby 有言在先 首先,我要感谢才华横溢的Tim“spacemonkey Appleby允许本教程中使用他那个极其NB的Sagat模型.不 ...

  3. ASP.NET MVC 视图(四)

    ASP.NET MVC 视图(四) 前言 上篇对于利用IoC框架对视图的实现进行依赖注入,最后还简单的介绍一下自定义的视图辅助器是怎么定义和使用的,对于Razor语法的细节和辅助器的使用下篇会说讲到, ...

  4. ABP(现代ASP.NET样板开发框架)系列之16、ABP应用层——数据传输对象(DTOs)

    点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之16.ABP应用层——数据传输对象(DTOs) ABP是“ASP.NET Boilerplate Project ...

  5. 微信小程序开发调试工具

    为了帮助开发者简单和高效地开发微信小程序,我们推出了全新的 开发者工具 ,集成了开发调试.代码编辑及程序发布等功能. 扫码登录 启动工具时,开发者需要使用已在后台绑定成功的微信号扫描二维码登录,后续所 ...

  6. [原创]MYSQL的简单入门

    MYSQL简单入门: 查询库名称:show databases; information_schema mysql test 2:创建库 create database 库名 DEFAULT CHAR ...

  7. 基本数据结构(1)——算法导论(11)

    1. 引言     从这篇博客开始,来介绍一些基本的数据结构知识.本篇及下一篇会介绍几种基本的数据结构:栈.队列.链表和有根树.此外还会介绍由数组构造对象和指针的方法.     这一篇主要介绍栈和队列 ...

  8. 构建ASP.NET MVC4+EF5+EasyUI+Unity2.x注入的后台管理系统(24)-权限管理系统-将权限授权给角色

    系列目录 过了个年回来,回顾一下,我们上次讲了角色管理,我们这一次来讲将权限授权给角色,这一节也是大家比较关心的.因为我们已经跑通了整个系统,知道权限的流转,我们先来看一张图 这张图主要分要3块,角色 ...

  9. ASP.NET MVC5+EF6+EasyUI 后台管理系统(54)-工作流设计-所有流程监控

    系列目录 先补充一个平面化登陆页面代码,自己更换喜欢的颜色背景 @using Apps.Common; @{ Layout = null; } <!DOCTYPE html> <ht ...

  10. JavaScript:正则表达式 前瞻

    正向前瞻:用来捕获出现在特定字符之前的字符,只有当字符后面跟着某个特定字符才去捕获它.(?=) 负向前瞻:它用匹配只有当字符后面不跟着某个特定字符时才去匹配它.(?!) 在执行前瞻和负向前瞻之类的运算 ...