自定义控件一

android系统原有的效果, 满足不了开发需求, 这时需要自定义一些效果, 使用的就是自定义控件.

  • 组合控件: 把一些原生的控件, 组合到一起, 达到某种特殊的效果.
  • 自定义控件:
  • 自己绘制View并控制逻辑
  • 继承已有View增强功能
  • 其他
  • android中所有的控件的形状都是: 矩形.
  • android中Tween动画的执行, 不会影响控件的位置. 点击事件还在哪里.

旋转动画

new RotateAnimation(
	float fromDegrees,//开始角度
 	float toDegrees, //结束角度
 	int pivotXType,//轴心X坐标类型-ABSOLUTE(绝对像素值),RELATIVE_TO_SELF(相对自身0-100%百分比),RELATIVE_TO_PARENT(相对父控件,0-100%百分比)
 	float pivotXValue,//轴心X坐标值
 	int pivotYType,//轴心Y坐标类型,取值同X坐标
 	float pivotYValue)轴心Y坐标值

ViewPager控件

android3.0以后才有的. 特点: 预加载功能.

android-support-v4.jar的源码位置:
sdk\extras\android\support\v4\src\java

高版本关联SDK源码即可:
sdk\sources\android-15\android

Android Private Libraries和Android Dependencies里依赖的jar无法添加源码,可以remove,然后把libs的jar包再add build path,同时要在Java Build Path--Order and export里打勾。
ViewPager的预加载
setOffscreenPageLimit(2);//修改缓存Item的数量。默认为1.不能小于1
  • 预加载左右两边的图片.
  • 如果发现左边图片的索引是负数, 小于0, 就不会预加载.
  • 如果发现右边的图片是索引是大于等于总item的个数,就不会预加载。
PagerAdapter
1. getCount() :返回了数据的size
2. isViewFromObject():是否使用缓存View。ViewPager管理Item是会把instantiateItem返回的object作为Item对应View的唯一标识。必须返回true,官方推荐view==Object。
3. instantiateItem():添加一个View到ViewPager
4. destroyItem:销毁一个Item。ViewPager里面移除了position对应的ImageView。

}
OnPageChangeListener

ViewPager页面切换监听

//监听页面切换状态 如果只想重写某个方法,可以使用SimpleOnPageChangeListener –空实现

	Viewpager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {

	//那个界面被切换到以后调用
	@Override
	public void onPageSelected(int index) {
	
	}
	//页面滑动的过程中调用。参数:当前操作页面索引/滑动的比例/滑动的距离
	@Override
	public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

	//手指操作屏幕的时候发生变化。 IDLE-0 滑动结束 DRAGGING-手指按下 SETTLING-2 手指抬起并发生了滑动
	 @Override
	public void onPageScrollStateChanged(int state) {
	
	}
});
ViewPager无限循环
Integer.MAX_VALUE;
	
	// 把ViewPager设置为默认选中Integer.MAX_VALUE / 2;
	
	int m = (Integer.MAX_VALUE / 2) % imageViewList.size();
	int currentPosition = Integer.MAX_VALUE / 2 - m;
	mViewPager.setCurrentItem(currentPosition);//希望currentPostion看起来是0.
ViewPager定时循环

使用handler实现

1.	onResume():handler.sendEmptyMessageDelayed(0, 5000);
	2.	onStop: handler.removeMessages(0);
	3.	handlerMessage(): 
			 mViewPager.setCurrentItem(mViewPager.getCurrentItem() + 1);
			 handler.sendEmptyMessageDelayed(0, 5000);
	4.	onPageScrollStateChanged:
		if(state==ViewPager.SCROLL_STATE_SETTLING){
	                handler.removeMessages(0);
	                handler.sendEmptyMessageDelayed(0, 5000);

PopupWindow

//显示在anchor下面。左上角与anchor左下角对齐。可设置x,y偏移
showAsDropDown(View anchor, int xoff, int yoff)

ListView Item点击事件无效的焦点问题

ImageButton抢焦点问题: 抢占了父控件的焦点, 自己可以被点击, 但是父控件不可以被点击.

解决方案:禁止子控件获取焦点

1. ViewGroup属性禁止子控件获取焦点。
		android:descendantFocusability="blocksDescendants"//三个选项
		a.	之前获取,如果不处理子控件还是会获取到焦点
		b.	之后获取,
		c.	禁止子控件获取
	2.设置抢焦点的View.setFocusable(false);

PPW.setBackgroundDrawable()连锁反应。

1. setBackgroundDrawable以后,PPW的contentView会被添加到一个PopupViewContainer布局上,PopupViewContainer作为popupView添加到windowManager上。
2. setOutsideTouchable(true)以后,popupView在add到WM上的时候会被添加WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH标记,该标记可以让popupView获取ACTION_OUTSIDE触摸事件
3. PopupViewContainer的onTouchEvent接收到ACTION_OUTSIDE事件就会执行dimiss方法。

PS:setBackgroundDrawable以后,contentView有了父控件,自然也可以设置动画了,因为Android中View的动画都是由父控件执行的。

自定义控件:

Android下View控件的大致绘制流程

measure  ->  layout     ->    draw
		 测量        布局(排版)         绘制
	   onMeasure     onLayout        onDraw

滑动开关实现步骤

继承自View的控件, 不包含子控件.

1. 定义一个类继承View的类并重写构造方法

	2. 定义对外提供的基本功能

	3. 重写onMeasure方法, 测量并设置View的宽和高

	4. 重写onDraw方法, 绘制当前控件要显示的元素
		
		- 先把背景图片画到控件的左上角上.
		- 把滑动块画在指定的位置上, 根据状态来绘制它的位置.

	5. 处理手指触摸时的事件. onTouchEvent
		
		- 在按下和移动时, 得到一个x轴按下的那个值.
		- 重新绘制滑动块的位置.
		- 抬起时, 判断当前的滑动块属于打开还是关闭, 把currentState重新赋值, 再重绘.

	6. 回调监听事件: 声明一个接口onSlideStateChangeListener#onSlideStateChange(boolean state);//在手指抬起并且开关状态变化的话,调用

自定义控件之自定义属性.

  1. 声明自定义属性: 在values目录下创建attrs.xml文件, 追加以下内容:
<declare-styleable name="ToggleView">

     <!-- 当前开关状态的属性, 取值类型为boolean-->
     <attr name="currentState" format="boolean" />

     <!-- 开关背景图片的属性, 取值类型为: R文件的引用id -->
     <attr name="switchBackgroundID" format="reference" />
     
     <!-- 开关滑动块的背景属性, 取值类型为: R文件的引用id -->
     <attr name="slideButtonBackgroundID" format="reference" />
 </declare-styleable>
  1. 在布局文件中使用自定义属性.
  • 在根节点的布局上, 声明命名空间:
// http://schemas.android.com/apk/res/ 固定写法, 标准前缀  后面跟着R文件的包名com.itheima.togglebutton
  xmlns:itheima="http://schemas.android.com/apk/res-auto
  • 在自定义控件中引用自定义属性:
itheima:currentState="true"
  itheima:switchBackgroundID="@drawable/switch_background"
  itheima:slideButtonBackgroundID="@drawable/slide_button_background"
  1. 在java代码中, 自定义控件的构造函数中, 取出自定义属性的值.
  • 通过属性名获取属性值
// 自定义属性的命名空间
  String namespace = "http://schemas.android.com/apk/res/com.itheima.togglebutton";
  
  // 取出自定义属性的值.
  // 1. 当前的状态
  boolean currentToggleState = attrs.getAttributeBooleanValue(namespace, "currentState", false);
  // 2. 取出背景图片的id
  int switchBackgroundID = attrs.getAttributeResourceValue(namespace, "switchBackgroundID", -1);
  setSwitchBackgroundID(switchBackgroundID);
  
  // 3. 取出滑动块图片的id
  int slideButtonBackgroundID = attrs.getAttributeResourceValue(namespace, "slideButtonBackgroundID", -1);
  setSlideButtonBackgroundID(slideButtonBackgroundID);
  • 通过属性索引获取属性值
//从attrs里获取styleable对应的属性结果集
  TypedArray ta = getContext().obtainStyledAttributes(attrs, R.styleable.ToggleButton);

  //通过attr的索引从结果集里获取值
  int switchBackgroundID = ta.getResourceId(R.styleable.ToggleButton_switchBackground, -1);

  int slideButtonBackgroundRes = ta.getResourceId(R.styleable.ToggleButton_slideButtonBackground, -1);

  boolean currentState = ta.getBoolean(R.styleable.ToggleButton_currentState, false);

  ta.recycle();//回收结果集

PS:AndroidStudio 的R文件路径–\build\generated\source\r\debug\com\itheima\togglebuttondemo\R.java