Android-自定义控件

1.autoCompleteTextview

  [1]在布局中声明
 
 
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. xmlns:tools="http://schemas.android.com/tools"
  4. android:id="@+id/activity_main"
  5. android:layout_width="match_parent"
  6. android:layout_height="match_parent"
  7. tools:context="com.itheima.myapplication.MainActivity">
  8. <AutoCompleteTextView
  9. android:id="@+id/actv"
  10. android:layout_width="match_parent"
  11. android:completionThreshold="1"
  12. android:layout_height="wrap_content" />
  13. </RelativeLayout>
 [2]这个控件展示数据原理和listview一样 需要一个适配器  代码如下:
 
 
  1. public class MainActivity extends AppCompatActivity {
  2. //声明一个数组 声明的内容就是这个控件要展示的内容
  3. private static final String[] COUNTRIES = new String[] {
  4. "老张", "老方", "老黎", "老毕", "老冯","老邱","aaa","aab"
  5. };
  6. @Override
  7. protected void onCreate(Bundle savedInstanceState) {
  8. super.onCreate(savedInstanceState);
  9. setContentView(R.layout.activity_main);
  10. //1.找到控件
  11. AutoCompleteTextView actv = (AutoCompleteTextView) findViewById(R.id.actv);
  12. //2.声明适配器 actv这个控件展示数据和listview一样 也需要适配器 参数2:使用系统提供好的一个布局
  13. ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
  14. android.R.layout.simple_dropdown_item_1line, COUNTRIES);
  15. //3.actv和适配器关联
  16. actv.setAdapter(adapter);
  17. }
  18. }
     
        
    1. ff


2.button和imageButton:

   


3.自定义控件的方式

 [1]通过原生的控件进行组合达到自定义的需求
 [2]定义一个类继承View
 [3]定义一个类继承ViewGroup

4.下拉列表
需求分析  通过edittext button  popupwindow listview组合达到自定义需求
代码实现步骤
[1]画UI 
 
 
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. xmlns:tools="http://schemas.android.com/tools"
  4. android:id="@+id/activity_main"
  5. android:layout_width="match_parent"
  6. android:layout_height="match_parent"
  7. android:gravity="center_horizontal"
  8. tools:context="itheima.a2_.MainActivity">
  9. <EditText
  10. android:id="@+id/et_number"
  11. android:layout_width="250dp"
  12. android:layout_height="wrap_content" />
  13. <ImageButton
  14. android:id="@+id/ib_down"
  15. android:layout_width="wrap_content"
  16. android:layout_height="wrap_content"
  17. android:layout_alignRight="@id/et_number"
  18. android:background="@null"
  19. android:src="@drawable/down_arrow" />
  20. </RelativeLayout>
[2]初始化popupwindow  
 
 
  1. //弹出一个popupwindow
  2. private void showpopUpWindow() {
  3. //0.准备一个listview 给popupwindow使用
  4. ListView view = initListView();
  5. //1.创建一个popupwindow
  6. // 参数1:View 指定popupwindow具体展示什么样的内容 -->展示listview
  7. //参数2 3:宽 和 高 popupwindow的宽和高 宽应该和edittext一样宽 高自定义
  8. PopupWindow pw = new PopupWindow(view,et_number.getWidth(),250,true);
  9. //2.展示出来 展示到edittext 下面
  10. pw.showAsDropDown(et_number);
  11. }
 [3]准备popupwindow要展示的内容--->listview  先画listview条目的布局
 
 
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. android:layout_width="match_parent"
  4. android:padding="5dp"
  5. android:layout_height="wrap_content">
  6. <ImageView
  7. android:layout_width="wrap_content"
  8. android:layout_height="wrap_content"
  9. android:src="@drawable/user"
  10. />
  11. <TextView
  12. android:id="@+id/tv_content"
  13. android:layout_width="0dp"
  14. android:layout_height="wrap_content"
  15. android:layout_weight="1"
  16. android:text="哈哈"
  17. android:gravity="center_horizontal"
  18. android:textSize="25sp"
  19. android:textColor="#000"
  20. />
  21. <ImageButton
  22. android:id="@+id/ib_delete"
  23. android:layout_width="wrap_content"
  24. android:layout_height="wrap_content"
  25. android:src="@drawable/delete"
  26. android:background="@null"
  27. />
  28. </LinearLayout>
 [4]展示listviet的数据
 
 
  1. private ListView initListView() {
  2. //1.通过打气筒把一个布局转换为一个view对象
  3. ListView listview = (ListView) View.inflate(getApplicationContext(), R.layout.listviewbg, null);
  4. //2.给listview指定分割线
  5. listview.setDivider(new ColorDrawable(Color.GRAY));
  6. //3.设置分割线高度
  7. listview.setDividerHeight(1);
  8. //2.把lists集合里面的数据展示到listview上
  9. listview.setAdapter(new MyAdapter());
  10. return listview;
  11. }
  12. //定义listview的适配器
  13. class MyAdapter extends BaseAdapter{
  14. //listview一共要展示多少个条目
  15. @Override
  16. public int getCount() {
  17. return lists.size();
  18. }
  19. @Override
  20. public Object getItem(int position) {
  21. return null;
  22. }
  23. @Override
  24. public long getItemId(int position) {
  25. return 0;
  26. }
  27. //获取一view 用来展示listview每个条目的内容
  28. @Override
  29. public View getView(int position, View convertView, ViewGroup parent) {
  30. //1.对listview优化
  31. View view;
  32. if (convertView == null){
  33. view = View.inflate(getApplicationContext(),R.layout.list_item,null);
  34. }else{
  35. view = convertView;
  36. }
  37. //2.找到条目的控件
  38. TextView tv_content = (TextView) view.findViewById(R.id.tv_content);
  39. ImageButton ib_delete = (ImageButton) view.findViewById(R.id.ib_delete);
  40. //3.更新数据
  41. tv_content.setText(lists.get(position));
  42. //4.给按钮设置点击事件
  43. ib_delete.setOnClickListener(new View.OnClickListener() {
  44. @Override
  45. public void onClick(View v) {
  46. Toast.makeText(MainActivity.this, "delete", Toast.LENGTH_SHORT).show();
  47. }
  48. });
  49. return view;
  50. }
  51. }
[5]点击listview的条目 把点击条目的数据取出来展示到edittext上
 
 
  1. listview.setOnItemClickListener(new AdapterView.OnItemClickListener() {
  2. @Override
  3. public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
  4. //6.把点击条目的数据取出来 数据在哪里存着就取哪里取
  5. String data = lists.get(position);
  6. //7.展示到EditText 控件上
  7. et_number.setText(data);
  8. //8.关闭popupwindow
  9. pw.dismiss();
  10. }
  11. });
注意:当listview 的条目上有button checkbox这些控件 会抢占条目的事件 ---->解决方案在条目的根布局上面加如下属性
 
 
  1. android:descendantFocusability="blocksDescendants"
[6]点击删除按钮逻辑
 
 
  1. ib_delete.setOnClickListener(new View.OnClickListener() {
  2. @Override
  3. public void onClick(View v) {
  4. //6.把对应条目删除 把数据先在集合里面删除
  5. lists.remove(position);
  6. //7.通知适配器更新
  7. notifyDataSetChanged();
  8. }
  9. });

5.view绘制流程

  [1]测量  
  measure方法完成对view测量 -->实际测量工作在onMeasure方法里面实现,因为measure方法是final的 -->最终调用setMeasuredDimension这个方法完成测量工作,当我们想自己对view进行测量的时候,重写 onMeasure方法,调用 setMeasuredDimension方法就可以了.
  [2]排版
       底层调用layout ---->setFrame方法完成对view摆放.
       view在屏幕占据的是一个矩形区域,view的职责是绘制和事件处理.
  [3]绘制 draw 
 
 
  1.                1. Draw the background 画背景
  2. * 2. If necessary, save the canvas' layers to prepare for fading 画图层 跳过
  3. * 3. Draw view's content 画内容 -->重写onDraw
  4. * 4. Draw children 画孩子 当继承ViewGropp的时候才涉及画孩子
  5. * 5. If necessary, draw the fading edges and restore layers 跳过
  6. * 6. Draw decorations (scrollbars for instance) 画滚动条
 [4]绘制实战
 4.1)画线
 
 
  1. canvas.drawLine(10,20,50,70,paint);
4.2) 画圆
 
 
  1. canvas.drawCircle(100,100,30,paint);
4.3)画图片
 
 
  1. Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.haha);
  2. canvas.drawBitmap(bitmap,0,0,null);
4.3)画三角形 三个点连接起来
 
 
  1. Path path = new Path(); ///创建一个路径 可以把几个点连接起来
  2. //定义三角形三个点
  3. int x1=100,y1=0;
  4. int x2 =0,y2=195;
  5. int x3=195,y3=195;
  6. //把上面三个点连接起来
  7. path.moveTo(x1,y1);
  8. path.lineTo(x2,y2);
  9. path.lineTo(x3,y3);
  10. path.lineTo(x1,y1);
  11. canvas.drawPath(path,paint);
4.4)画弧
 
 
  1. //5.画弧
  2. RectF rf = new RectF(5,5,195,195);
  3. canvas.drawArc(rf,0,mProgress,false,paint);

6.开关案例

   代码实现步骤
   [1]先在构造方法里面获取2张背景图片的宽和高
 
 
  1. //这个类在布局中使用
  2. public ToogleView(Context context, AttributeSet attrs) {
  3. super(context, attrs);
  4. //1.把准备好的2张图片 转换为bitmap对象
  5. toogleBg = BitmapFactory.decodeResource(getResources(), R.drawable.toogle_background);
  6. slideBg = BitmapFactory.decodeResource(getResources(), R.drawable.toogle_slidebg);
  7. }
  [2]重写onMeasure方法对当前view进行测量  当前view的宽高和 toogleBg 一样
 
 
  1. //对当前的view 自己测量 当前view的宽就是toogleBg的宽 view的高就是toogleBg的高
  2. @Override
  3. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  4. //自己测量
  5. setMeasuredDimension(toogleBg.getWidth(),toogleBg.getHeight());
  6. }
 [3]重写onDraw方法往当前的view上画内容 其实就是画图片
 
 
  1. //代表往当前view上画内容--->实际上就是画图片
  2. @Override
  3. protected void onDraw(Canvas canvas) {
  4. //1.画开关背景
  5. canvas.drawBitmap(toogleBg,0,0,null);
  6. //2.画滑动块背景
  7. canvas.drawBitmap(slideBg,0,0,null);
  8. }
[4]给开关定义监听事件  具体什么时候出发回调事件:
 
 
  1. //定义接口回调
  2. public interface OnToogleListener{
  3. //回调方法
  4. void onState(boolean state);
  5. }
  6. //定义监听器方法
  7. public void setOnToogleListener(OnToogleListener listener){
  8. //接收外面传递过来的实例
  9. this.mListener = listener;
  10. }
[5]处理滑动块滑动的逻辑  重写onTouchEvent方法 处理手指按下和移动的逻辑
 
 
  1. @Override
  2. public boolean onTouchEvent(MotionEvent event){
  3. //1.获取触摸事件类型
  4. int action = event.getAction();
  5. switch (action){
  6. case MotionEvent.ACTION_DOWN: //按下
  7. //1.获取手指按下的坐标
  8. downX = event.getX();
  9. break;
  10. case MotionEvent.ACTION_MOVE: //移动
  11. //1.获取移动的坐标
  12. float movex = event.getX();
  13. //2.算出移动距离
  14. float distancX = movex - downX;
  15. //3.对slideLeftPosition 重新赋值
  16. slideLeftPosition+=distancX;
  17. //4.对滑动块的边界解析判断
  18. if (slideLeftPosition <=0){
  19. slideLeftPosition = 0;
  20. }else if(slideLeftPosition >=slideRightMaxPosition){
  21. slideLeftPosition = slideRightMaxPosition;
  22. }
  23. //5.改变一下起始点坐标
  24. downX = movex;
  25. break;
  26. case MotionEvent.ACTION_UP: //抬起
  27. break;
  28. }
  29. invalidate();//--->onDraw方法就会执行
  30. return true; //让当前控件消费事件
  31. }
[6]处理手指抬起的业务逻辑
 
 
  1. case MotionEvent.ACTION_UP: //抬起
  2. //1.获取开关背景中心点
  3. float toogleBgCenter = toogleBg.getWidth()/2;
  4. float slideBgCenter =slideLeftPosition + slideBg.getWidth()/2;
  5. //2.做判断
  6. if (slideBgCenter <= toogleBgCenter){
  7. //说明开关是关闭状态
  8. slideLeftPosition = 0;
  9. }else{
  10. slideLeftPosition = slideRightMaxPosition;
  11. }
  12. break;
[7]实现开关的功能.
 
 
  1. //3.如果手指抬起了 实现开关 开关的功能
  2. if (isHandup){
  3. isHandup = false;
  4. //4.当手指抬起的时候获取当前开关的状态
  5. boolean isOpenTemp = slideLeftPosition > 0;
  6. //5.对开关的状态进行判断 判断开关的状态是否发生了变化
  7. if (isOpen!=isOpenTemp && mListener!=null){
  8. //说明开关的状态发生了改变 并且监听器实例不为空 触发接口的回调方法
  9. mListener.onState(isOpenTemp);
  10. //6.改变开关默认状态
  11. isOpen = isOpenTemp;
  12. }
  13. }
[8]自定义开关的属性 
   8.1)在res下定义一个attrs文件
 
 
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <resources>
  3. <declare-styleable name="toogleView">
  4. <attr name="state" format="boolean" />
  5. </declare-styleable>
  6. </resources>
 8.2)自己声明一个命名空间.
 
 
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. xmlns:itheima="http://schemas.android.com/apk/res-auto"
  4. xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main"
  5. android:layout_width="match_parent" android:layout_height="match_parent"
  6. tools:context="com.itheima.toogleview.MainActivity">
  7. <com.itheima.toogleview.ToogleView
  8. android:id="@+id/toogleview"
  9. android:layout_width="wrap_content"
  10. itheima:state="false"
  11. android:layout_centerInParent="true"
  12. android:layout_height="wrap_content" />
  13. </RelativeLayout>
 8.3)在构造方法里面获取我们声明的属性值
 
 
  1. //3.获取布局文件中声明的属性值
  2. String nameSpace = "http://schemas.android.com/apk/res-auto";
  3. boolean state = attrs.getAttributeBooleanValue(nameSpace, "state", false);
 8.4)更新开关的状态
 
 
  1. //更新开关的状态
  2. private void setToogleState(boolean state) {
  3. if (state){
  4. //说明开关是开的状态
  5. slideLeftPosition = slideRightMaxPosition;
  6. }else{
  7. //说明开关是关的状态
  8. slideLeftPosition = 0;
  9. }
  10. }


    
   
    

   


  
    
  





 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值