博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Android中的自定义控件(一)
阅读量:5144 次
发布时间:2019-06-13

本文共 17116 字,大约阅读时间需要 57 分钟。

       自定义控件是根据自己的需要自己来编写控件。安卓自带的控件有时候无法满足你的需求,这种时候,我们只能去自己去实现适合项目的控件。同时,安卓也允许你去继承已经存在的控件或者实现你自己的控件以便优化界面和创造更加丰富的用户体验。在平常的项目中,我们 人为的把自定义控件分为两种:一种是组合方式实现。一种是通过继承view或viewgroup及其子类实现。两者都可以实现我们想要的效果,因此,我们可以根据自己的需求,选择合适的方案。本文以案例的形式来显示几种较为常见的自定义控件。

   案例一:   优酷菜单

     功能介绍: 手机界面的底部中央有一个半圆,初始状态显示三级菜单,由外到内分别是第三级菜单,第二级菜单,第三级菜单。每个菜单中有一些按钮,可以用3个容器(RelativeLayout)来表示3个菜单,往里面添加按钮即可。当再次点击中心位置的图片式,隐藏外面两层的的条目。再次点击重心位置的图片时,旋转出二级菜单。点击二级菜单中心位置时,旋转出三级菜单,再次点击则隐藏三级菜单。以上就是优酷菜单要实现的功能。

   实现步骤:

      1. 完成布局文件。由于控件较多所以优酷菜单的布局较为复杂,但是可以在一个根布局设置三个子线性布局来包裹每一个小的控件。通过调整margin值来是使得界面更加的美观。注意3个菜单布局的顺序,先放men3,然后menu2,然后menu1,顺序不能反,因为越往后放的菜单越在界面的上方,这样面积最小的菜单1会在最上方,菜单1不会遮挡到下面的菜单2和菜单3。
      2. 完成布局文件后,创建一个类,在该类中实现业务逻辑。首先要创建两个布尔类型的常量存储菜单的状态。通过swich语句判断被点击的控件是二级菜单还是一级菜单。如果是一级菜单的中心被点击,则判断三级菜单是否打开。如果三级菜单打开着,则隐藏一级菜单和二级菜单,并修改状态。如果三级菜单隐藏着则判断二级菜单是否隐藏。如果二级菜单打开着,则隐藏二级菜单。如果二级菜单隐藏则打开二级菜单。如果是二级菜单的中心被点击,则只需要判断三级菜单是否隐藏。如果三级菜单打开着,则隐藏三级菜单。如果三级菜单隐藏则打开三级菜单。同时还要修改菜单的状态。
      3. 接下来可以在一个工具类中实现动画的效果。步骤2中的隐藏和显示均都是通过动画效果实现的。这里的动画效果用到的是补间动画,为RotateAnimation对象传入起始角度,结束的角度,参考对象和参考点坐标。一个小细节是将setFillAfter属性设为true,表示从结束位置开始动画,避免动画结束后又回到起始位置。隐藏和显示区别是旋转的起始角度和结束角度,这里显示设定的起始角度是-180,结束角度为0。隐藏设定的起始角度为0,结束角度为-180。当然也可以设定为其他值,取决于自己想要的效果。
      4. 代码优化。快速点击按钮时会发现菜单还没隐藏完就开始显示了,或者还没显示完又隐藏了。解决方案:监听动画的开启次数来判断是否需要执行画,给动画类添加一个监听器。设置一个成员变量。当开始开始动画是执行加一操作,结束动画是执行减一操作。这样只有当常量为零时,才没有动画。因此swich语句中加入判断如果当前存在动画,等待动画执行完之后在进入下面的逻辑。

       菜单选择界面的布局:

       主界面的布局,在代码中用打气筒把菜单布局打到主界面上。

      主程序是空实现。

import android.os.Bundle;import android.support.v7.app.AppCompatActivity;public class Selectbar extends AppCompatActivity {    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_selectbar);    }}

      主要逻辑的实现。

import android.content.Context;import android.util.AttributeSet;import android.util.Log;import android.view.View;import android.widget.RelativeLayout;import com.example.selectbar.com.example.selectbar.utils.utils;/** * Created by huang on 2016/11/22. */public class Rotate extends RelativeLayout implements View.OnClickListener {    private static final String TAG = "Rotate";    private RelativeLayout rl_menu1, rl_menu2, rl_menu3;    private boolean menu2showing = true;    private boolean menu3showing = true;    public Rotate(Context context) {        super(context,null);    }    public Rotate(Context context, AttributeSet attrs) {        super(context, attrs);        initdata(context);    }    private void initdata(Context context) {        Log.i(TAG, "initdata: text");        View view = View.inflate(context,R.layout.activity_rolate,null);        view.findViewById(R.id.btn_menu1).setOnClickListener(this);        view.findViewById(R.id.btn_menu2).setOnClickListener(this);        rl_menu1 = (RelativeLayout) view.findViewById(R.id.rl_menu1);        rl_menu2 = (RelativeLayout) view.findViewById(R.id.rl_menu2);        rl_menu3 = (RelativeLayout) view.findViewById(R.id.rl_menu3);        addView(view);    }    @Override    public void onClick(View v) {        switch (v.getId()) {            case R.id.btn_menu2:                if (utils.hasAnimationexcuting()) {                    return;                }                if (menu3showing) {                    utils.hiden(rl_menu3);                } else {                    utils.show(rl_menu3);                }                menu3showing = !menu3showing;                break;            case R.id.btn_menu1:                if (utils.hasAnimationexcuting()) {                    return;                }                if (menu3showing) {                    utils.hiden(rl_menu3);                    menu3showing = false;                    utils.hiden(rl_menu2, 300);                } else if (menu2showing) {                    utils.hiden(rl_menu2);                } else {                    utils.show(rl_menu2);                }                menu2showing = !menu2showing;                break;        }    }}

       还需要一个工具类实现动画的效果。      

import android.view.View;import android.view.ViewGroup;import android.view.animation.Animation;import android.view.animation.RotateAnimation;/** * Created by huang on 2016/11/22. */public class utils {    private static final String TAG = "utils";    private static void setviewclickable(View view, boolean clickable) {        view.setClickable(clickable);        if (view instanceof ViewGroup) {            ViewGroup viewgroup = (ViewGroup) view;            for (int i = 0; i < viewgroup.getChildCount(); i++) {                View child = ((ViewGroup) view).getChildAt(i);                child.setClickable(clickable);            }        }    }    public static void hiden(View view) {        float fromDegreeas = 0;        float toDegrees = -180f;        rotateview(view, fromDegreeas, toDegrees, 0l);        setviewclickable(view, false);    }    public static void hiden(View view, long startoffset) {        float fromDegreeas = 0;        float toDegrees = -180f;        rotateview(view, fromDegreeas, toDegrees, startoffset);        setviewclickable(view, false);    }    public static void show(View view) {        float fromDegrees = -180f;        float toDegrees = 0;        rotateview(view, fromDegrees, toDegrees, 0l);        setviewclickable(view, true);    }    public static boolean hasAnimationexcuting() {        return  startcount > 0;    }    public static void rotateview(View view, float fromDegrees, float toDegrees, long startoffset) {        int pivotXType = RotateAnimation.RELATIVE_TO_SELF;        int pivotYType = RotateAnimation.RELATIVE_TO_SELF;        float pivotXValue = 0.5f;        float pivatYValue = 1.0f;        RotateAnimation ra = new RotateAnimation(fromDegrees, toDegrees, pivotXType, pivotXValue, pivotYType, pivatYValue);        ra.setDuration(500);        ra.setFillAfter(true);        ra.setStartOffset(startoffset);        ra.setAnimationListener(listener);        view.startAnimation(ra);    }    public static int startcount;    static Animation.AnimationListener listener = new Animation.AnimationListener() {        @Override        public void onAnimationStart(Animation animation) {            startcount++;//            Log.i(TAG, "onAnimationStart: " +startcount);        }        @Override        public void onAnimationEnd(Animation animation) {            startcount--;//            Log.i(TAG, "onAnimationEnd: " + startcount);        }        @Override        public void onAnimationRepeat(Animation animation) {        }    };}

 

 

      案例二   popupwindow实现下拉列表:

      功能介绍:创建一个textview,当我们点击textview时会弹出一个列表,列表中存放的是数字信息和一张删除图片。列表是用listview实现。点击删除图片可以将本条记录从列表中删除。本案例的有两种,一种是通过动画的缩放实现弹出效果,另一种是采取popupwindow。本文采用的是第二种方案。

     实现步骤:

  1. 添加布局文件。首先要在界面上放置一个textview,在textview的右边放置一个下拉图片。为了更方便地摆放控件,可以采用相对布局,这样只要保证imageview和textview上对齐、下对齐、右对齐就可以实现要现实的效果。为了是界面更美观,可以把图片设为透明的,即背景为null。这里将popupwindow弹出来的界面用listview表示。因此还要写一个单独的listview以及每一个条目的布局,然后用打气筒打到textview的下方、
  2. Pupupwindow的编写。Popupwindow的编写有两个关键的步骤,一个是new一个pupupwimdow对象,将要显示的布局、布局的高和宽、是否可聚等相关的参数传入。这里pupupwindow的显示界面是用的打气筒把listview加载进来。同时listview条目的布局也用到inflate。因此用到两次打气筒。同时为每一个listview的条目设置条目点击的监听事件,这样在条目被点击时候可以将条目的内容显示在textview中。。Popupwindow的编写另一个关键性的步骤是showAsDropDown,即将要显示在哪个控件下方,偏移量多少出传进来。需要注意的是控件的实际宽高和看到的宽高有一定的误差,只是因为控件的背景用一定的宽度。还有一点就是,按返回键时PopupWindow没法隐藏,给PopupWindow设置一个背景即可解决这个问题,如下:popupWindow.setBackgroundDrawable(new ColorDrawable())。每次显示PopupWindow时都创建一个新的PopupWindow对象,其实可以复用一个PopupWindow对象的。即每次创建前先判断popupwindow是否为空。
  3. Listiview条目的编写较为定。这里做了两处的优化。一个是服用conterview,开始操作之前判断conterview是否为空。若为空创建一个新的view,否则复用被回收的conterview。另一处的优化是,每一次finviewbyid会消耗大量的内存。因此可以单独定义一个类,存放控件,在cnterview为空时创建一次。每次调用时只需要调用定义类中的控件。还有一点是,要是条目上的删除图标有用还要对删除图片设定一个点击事件。一个小细节是,当listvie条目中存在button及其子类时,会抢占焦点。因此这里删除图片的标签用的是imageview。当然你也可以采取其他方式,比如,在条目的根布局加上这个属性:android:descendantFocusability="blocksDescendants"。

         主界面的布局:

         listview条目的布局

       单独写一个listview。

 

       主程序中的逻辑。

import android.graphics.drawable.ColorDrawable;import android.os.Bundle;import android.support.v7.app.AppCompatActivity;import android.view.View;import android.widget.AdapterView;import android.widget.EditText;import android.widget.ListView;import android.widget.PopupWindow;public class MainActivity extends AppCompatActivity {    private EditText et_number;    private PopupWindow popupwindow;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        et_number = (EditText) findViewById(R.id.et_number);    }    public void showNumberListToggle(View view) {        if (popupwindow == null) {                    //复用popupwindow            View contentView = createContent();            int width = et_number.getWidth() - 4;            int height = 400;            boolean focusable = true;            popupwindow = new PopupWindow(contentView, width, height, focusable);            popupwindow.setBackgroundDrawable(new ColorDrawable());        }        View anchor = et_number;        int xoff = 2;        int yoff = -5;        popupwindow.showAsDropDown(anchor, xoff, yoff);    }    private View createContent() {            ListView lv = (ListView) View.inflate(this, R.layout.activity_list, null);            lv.setAdapter(new NumberListAdapter());            lv.setVerticalScrollBarEnabled(false);            lv.setOnItemClickListener(monitemclickListener);        return lv;    }    AdapterView.OnItemClickListener monitemclickListener = new AdapterView.OnItemClickListener() {        @Override        public void onItemClick(AdapterView
parent, View view, int position, long id) { String number = (String) parent.getItemAtPosition(position); et_number.setText(number); popupwindow.dismiss(); } };}

       适配器的编写

import android.view.View;import android.view.ViewGroup;import android.widget.BaseAdapter;import android.widget.ImageView;import android.widget.TextView;import java.util.ArrayList;/** * Created by huang on 2016/11/22. */public class NumberListAdapter extends BaseAdapter {   private ArrayList
numbers = new ArrayList(); { for (int i = 0; i < 30; i++) { numbers.add(10000 + i + "" ); } } @Override public int getCount() { return numbers.size(); } @Override public Object getItem(int position) { return numbers.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(final int position, View convertView, ViewGroup parent) { View view; viewHolder holder; if(convertView == null){ view = View.inflate(parent.getContext(),R.layout.item_number_list,null); holder = new viewHolder(); holder.ib_del = (ImageView) view.findViewById(R.id.ib_del); holder.tv_number = (TextView) view.findViewById(R.id.tv_numer); view.setTag(holder); }else{ view = convertView; holder = (viewHolder)view.getTag(); } holder.ib_del.setImageResource(R.mipmap.delete); holder.tv_number.setText(numbers.get(position)); holder.ib_del.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { numbers.remove(position); notifyDataSetChanged(); } }); return view; } static class viewHolder{ TextView tv_number; ImageView ib_del; }}

 

     

      案例三 viewpager实现轮播图:  

      功能介绍:  广告条的实现,又被称为轮播图。在手机界面的最上方放置一个ViewPager功能,实现界面的左右滑动。滑动又分为两种,一种是间隔一定的时间自动滑动,同时还支持手势的左右滑动。一个小细节是在轮播图底部放置和图片数量相等的小点,用于表示当前哪个图片被选中。
      实现步骤:
        1. 布局文件有两部分组成,一个是viewpager,一个是底部的条和小圆点。Viewpager存在于v4包下,因此可兼容到Android1.6。底部的条和小圆点放在一个线性布局中,让放在viewpager的底部,同时文字和圆点为中心位置。这里特别说一下小圆点的实现。小圆点使用shape图做出来的。由于无法再布局文件中获取圆点的个数,因此可以在布局文件中放置一个LinearLayout,然后在java代码中将view对象向该LinearLayout中添加。同时将白点和黑点的状态选择器设置为view的背景。
        2. 接下来是viewpager的编写。编写viewpager的关键是写适配器。写一个类继承PagerAdapter,重写其中的方法。Viewpager默认是加载三张图片,屏幕中间一张,屏幕左边和屏幕右边各一张。我们希望轮播图能够循环的播放,因此可以给getcount返回一个足够大的值,初始化轮播图时,将轮播图的初始位置设置在中间,这样就可以实现循环播放。当我们把getcount返回值设为一个很大的值时,在instantiateItem中也要对position进行判断。传入的的position是一个极大的值,但是图片的数量确实有限的,因此可以对position取余,这样就可以把position的位置和图片的数量对应起来。
        3. 实现自动播放需要用到消息机制,间隔三秒调用显示下一张图片的方法。显示下一张图片是调用viewpager的setCurrentItem,传入下一个图片的item数。手指滑动时viewpager自身就有的功能,因此不需要再定义。
        4. 最后要实现的功能是,viewpager底部的文字和小圆点跟随图片变化。这时候可以设定一个viewpager的监听事件,当图片发生改变时改变textview的值。然后遍历linearlayout的子view(子view中存发的是小圆点),当圆点的序列号和图片的序列号一直是,则选中圆点。当然,这里同样要对position取余,将position转化到于图片大小一致的范围。

      主界面的实现:

       状态选择器

       shape图的绘制

<节省空间, 两个写下一起>

       主程序中的业务逻辑

import android.os.Bundle;import android.os.Handler;import android.support.v4.view.ViewPager;import android.support.v7.app.AppCompatActivity;import android.util.Log;import android.view.View;import android.widget.LinearLayout;import android.widget.TextView;public class MainActivity extends AppCompatActivity {    private static final String TAG = "MainActivity";    private int[] imageResids = {R.drawable.a, R.drawable.b, R.drawable.c, R.drawable.d, R.drawable.e};    private String[] desc = {            "巩俐不低俗,我就不能低俗",            "扑树又回来啦!再唱经典老歌引万人大合唱",            "揭秘北京电影如何升级",            "乐视网TV版大派送",            "热血屌丝的反杀"    };    private LinearLayout ll_dots;    private TextView tv_desc;    private ViewPager viewpager;    private static final int SHOW_NEXT_PAGE = 0;    private Handler handler = new Handler() ;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        ll_dots = (LinearLayout) findViewById(R.id.ll_dots);        tv_desc = (TextView) findViewById(R.id.tv_desc);        viewpager = (ViewPager) findViewById(R.id.view_pager);        viewpager.setAdapter(new BannerAapter(imageResids));        viewpager.setOnPageChangeListener(listener);        initDot();        changeDotandDesc(0);        viewpager.setCurrentItem(viewpager.getAdapter().getCount() / 2);        handler.postDelayed(mRunnable,3000);    }    Runnable mRunnable = new Runnable() {        @Override        public void run() {            shownextpage();        }    };    /**初始化小圆点*/    private void initDot() {        for (int i = 0; i < imageResids.length; i++) {            int _5dp = dp2px(5);            LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(_5dp, _5dp);            params.leftMargin = _5dp;            View dot = new View(this);            dot.setLayoutParams(params);            dot.setBackgroundResource(R.drawable.select_dot);            ll_dots.addView(dot);        }    }    /**将pd数据转化为px数据*/    private int dp2px(int pd) {        float desity = getResources().getDisplayMetrics().density;        return (int) (pd * desity + 0.5f);    }    ViewPager.OnPageChangeListener listener = new ViewPager.OnPageChangeListener() {        @Override        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {        }        @Override        public void onPageSelected(int position) {            changeDotandDesc(position);        }        @Override        public void onPageScrollStateChanged(int state) {        }    };    public void changeDotandDesc(int position) {        position = position % ll_dots.getChildCount();        tv_desc.setText(desc[position]);        for (int i = 0; i < ll_dots.getChildCount(); i++) {            Log.i(TAG, "changeDotandDesc: " + i+ "    "  + position);            ll_dots.getChildAt(i).setSelected(i == position);        }    }    private void shownextpage() {        int currenItem = viewpager.getCurrentItem();        viewpager.setCurrentItem(currenItem + 1);        handler.postDelayed(mRunnable,3000);    }}

 

     适配器的编写:

import android.support.v4.view.PagerAdapter;import android.view.View;import android.view.ViewGroup;import android.widget.ImageView;/** * Created by huang on 2016/11/22. */public class BannerAapter extends PagerAdapter {    private int[] imageResIds;    public BannerAapter(int[] imageResIds) {        this.imageResIds = imageResIds;    }    @Override    public int getCount() {        return imageResIds.length * 10000 * 100;    }    @Override    public boolean isViewFromObject(View view, Object object) {        return view == object;    }    @Override    public Object instantiateItem(ViewGroup container, int position) {        ImageView imageview = new ImageView(container.getContext());        position = position % imageResIds.length;        imageview.setBackgroundResource(imageResIds[position]);        container.addView(imageview);        return imageview;    }    @Override    public void destroyItem(ViewGroup container, int position, Object object) {        container.removeView((ImageView) object);    }}

 

 

 

    

 

转载于:https://www.cnblogs.com/huangjie123/p/6089140.html

你可能感兴趣的文章
网络编程之socket
查看>>
Maven pom项目部署
查看>>
Cognos报表验证(添加字段)
查看>>
学术-物理-维空间:一维空间
查看>>
CSS:CSS 实例
查看>>
innodb的存储结构
查看>>
焦点控制
查看>>
python-文件读写操作
查看>>
P1129 [ZJOI2007]矩阵游戏 二分图匹配
查看>>
Git 内部原理之 Git 对象哈希
查看>>
Vue中引入TradingView制作K线图
查看>>
爱历史 - 朝代歌
查看>>
【笔记】Cocos2dx学习笔记
查看>>
PHP设计模式之:单例模式
查看>>
c++输出缓冲区刷新
查看>>
Linux查看CPU和内存使用情况总结
查看>>
session丢失问题
查看>>
Python 批量修改root密码
查看>>
ThinkSNS+ 基于 Laravel master 分支,从 1 到 0,再到 0.1
查看>>
WEB服务器:Apache、Tomcat、JBoss、WebLogic、Websphere、IIS的区别与关系
查看>>