Android梳理不常用widget篇

闲得蛋疼,折腾折腾android源码包下面widget包下面很少用以及几乎没听说过的控件(针对自己而言,莫要批斗(⊙o⊙)哦丫)。


TextClock

效果图

Android梳理不常用widget篇_第1张图片

实现原理

TextClock继承自TextView,扩展自定义属性三个(主要是显示格式相关属性控制)

Android梳理不常用widget篇_第2张图片

在onAttachedToWindow和onDetachedFromWindow方法注册了广播和取消注册,监听时间发生变化了,接收到广播,调用onTimeChanged方法更新UI,下面是简要代码块


    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();

        if (!mAttached) {
            mAttached = true;

            registerReceiver();
           // .................略....................
        }
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();

        if (mAttached) {
            unregisterReceiver();
           // .................略....................
        }
    }

    private void registerReceiver() {
        final IntentFilter filter = new IntentFilter();
        filter.addAction(Intent.ACTION_TIME_TICK);
        filter.addAction(Intent.ACTION_TIME_CHANGED);
        filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
        getContext().registerReceiver(mIntentReceiver, filter, null, getHandler());
    }

    private void unregisterReceiver() {
        getContext().unregisterReceiver(mIntentReceiver);
    }

    private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (mTimeZone == null &&    Intent.ACTION_TIMEZONE_CHANGED.equals(intent.getAction())) {
                final String timeZone = intent.getStringExtra("time-zone");
                createTime(timeZone);
            }
            onTimeChanged();
        }
    };
    private void onTimeChanged() {
        mTime.setTimeInMillis(System.currentTimeMillis());
        setText(DateFormat.format(mFormat, mTime));
        setContentDescription(DateFormat.format(mDescFormat, mTime));
    }

Space

效果图

Android梳理不常用widget篇_第3张图片Android梳理不常用widget篇_第4张图片

实现原理

Space控件继承自View,在构造函数直接把控件设置为INVISIBLE模式,修改测量方法onMeasure,取消了draw方法的canvas绘制渲染的处理,下面是测量宽高相关核心代码

 private static int getDefaultSize2(int size, int measureSpec) {
        int result = size;
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);

        switch (specMode) {
            case MeasureSpec.UNSPECIFIED:
                result = size;
                break;
            case MeasureSpec.AT_MOST:
                result = Math.min(size, specSize);
                break;
            case MeasureSpec.EXACTLY:
                result = specSize;
                break;
        }
        return result;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(
                getDefaultSize2(getSuggestedMinimumWidth(), widthMeasureSpec),
                getDefaultSize2(getSuggestedMinimumHeight(), heightMeasureSpec));
    }

如果在以前你喜欢用View控件,setVisiblity占用屏幕空间,建议以后替换成Space控件,好处在于draw方法的重写,具体不解释。

ZoomButton 、ZoomControls

效果图

Android梳理不常用widget篇_第5张图片

实现原理

ZoomButton内部就一个核心点,设置ZoomSpeed,默认速度值1000,ZoomControl是一个自定义的ViewGroup,布局控件默认inflater预先定义好的布局,如下

<merge xmlns:android="http://schemas.android.com/apk/res/android">
    <ZoomButton android:id="@+id/zoomOut" 
        android:background="@android:drawable/btn_zoom_down" 
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        />
    <ZoomButton android:id="@+id/zoomIn" 
        android:background="@android:drawable/btn_zoom_up"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        />
merge>

如果你想改变着丑陋的UI,你可以尝试拷贝源码出来,修改Inflater的布局即可。这个空间提供的show 、hide方法只是在执行透明动画,关键代码块如下

  public void show() {
        fade(View.VISIBLE, 0.0f, 1.0f);
    }

    public void hide() {
        fade(View.GONE, 1.0f, 0.0f);
    }

    private void fade(int visibility, float startAlpha, float endAlpha) {
        AlphaAnimation anim = new AlphaAnimation(startAlpha, endAlpha);
        anim.setDuration(500);
        startAnimation(anim);
        setVisibility(visibility);
    }

该类对ZoomSpeed值提供了一个方法setZoomSpeed设置,其他的没什么好关注的,Listener的监听回调也略过吧,就这样吧。(merge没用过的可以去了解一下)

AdapterViewAnimator、AdapterViewFlipper、StackView

效果图

Android梳理不常用widget篇_第6张图片

实现原理

AdapterViewFlipper可以实现轮播图片,虽然有点撇脚,不能手动滑动,也无法感知轮播到了那个界面,关于这一点可以重写控件,修改showNext回调位置,至于手动滑动需要手势拦截处理这块怎么说呢,你若有兴趣自己造轮子可以试试。

这里要区别两个类:ViewFlipper和AdapterViewFlipper,这是两个不同的类,下面是他们的继承体系

Android梳理不常用widget篇_第7张图片 Android梳理不常用widget篇_第8张图片

AdapterViewFlipper的自定义属性在他的父类有定义自己也有定义,下面来看看具体的含义与代码调用方法

Android梳理不常用widget篇_第9张图片Android梳理不常用widget篇_第10张图片

下面是对照自定义属性的说明和方法调用

自定义属性 注释 对应方法
inAnimation 组件显示时用的动画 setInAnimation()
outAnimation 组件隐藏时用的动画 setInAnimation()
animateFirstView 组件显示第一个View时是否播放动画 setAnimateFirstView(),默认播放动画
loopViews 循环到最后一个视图是否回到第一个视图 AdapterViewFlipper强制设为了true
flipInterval 循环播放动画时间间隔 setFlipInterval()默认1000间隔
autoStart 是否自动播放动画 setAutoStart()

当你使用默认播放动画切换视图感觉不是1秒,不要觉得奇怪,动画执行也需要时间的,默认in 、out 动画时常都是200。对于代码功能性调用来说我们只需要关注下面这几个方法即可

Android梳理不常用widget篇_第11张图片

startFlipping开启轮播没啥好说的,该类同样使用了上面提到的注册广播,注册了屏幕关闭和唤醒广播,关闭屏幕就暂停轮播,唤醒时如果之前是轮播状态,那么继续开始轮播。(相关代码块就不贴,自己查看源代码)

StackView虽说也是AdapterViewAnimator子类,但是其实现效果与AdapterViewFlipper不同,具体效果图就不贴了,实在是原生控件在实际得出的效果拿不出手,而且感觉在应用场景上来说也找不到对应的,于是乎在github检索了一会,找到了一个还算不错的开源,下面是地址和效果图:https://github.com/blipinsk/FlippableStackView

QuickContactBadge

这个ImageView的扩展可以简单的了解一下,推荐农民伯伯大神博客地址:http://www.cnblogs.com/over140/archive/2010/09/28/1837287.html,开发中用的比较少,大概看看就行了

ViewAnimator、ViewFlipper、ViewSwitcher、TextSwitcher

ViewFlipper、ViewSwitcher都是继承自ViewAnimator,ViewFlipper与AdapterViewFlipper差别不大略过,这里主要是关注一下ViewSwitcher相关类扩展一下视野。ViewAnimator是继承自FrameLayout,而ViewSwitcher是其子类,强制有且只能有2个childView,TextSwitcher限定childView必须是TextView.关于这类扩展控件自行检索Github.

Spinner、AbsSpinner、AdapterView、AutoCompleteTextView、MultiAutoCompleteTextView、ListPopupWindow、ListView到底有多亲密的关系?

直接贴代码估计比较晦涩难懂了,能把人给绕晕了,我们还是先看图说话吧。

Android梳理不常用widget篇_第12张图片

他们之间的的关系到底有多深呢,且听我一一道来。最上层ViewGrou抽象下来的Adapter,拥有很多子类,Spinner的具体实现,跟踪源代码发现内部有实例化出一个ListPopuWindow、DialogPopup(根据不同的mode选择不同的实例,本质区别一个是popuwindow一个是AlertDialog),而ListPopuWindow类让我发现了这么个东西DropDownListView 的实例(AlertDialog同样调用了getLitView方法)

  private static class DropDownListView extends ListView {

  }     

由此可以知道每个ListPopuWindow里面有一个LIstView,Spinner弹出来的其实就是ListView绑定适配器,而Spinner内部定义了一个DropDownAdapter,ListView绑定Adapter,最后调用各自mode对应的popu.show就可以了。那么问题来了,Spinner在很么时候弹出ListPopuWindow?带着这个问题继续跟踪Spinner源码

ViewGroup继承自View,所以可以共享父类方法performClick,点击时会调用该方法,自然Spinner也不例外,具体调用代码块如下(当然还有其他地方有触发,这里暂不深入理解了)


    @Override
    public boolean performClick() {
        boolean handled = super.performClick();

        if (!handled) {
            handled = true;

            if (!mPopup.isShowing()) {
                mPopup.show(getTextDirection(), getTextAlignment());
            }
        }

        return handled;
    }

用过的人都知道Spinner的Item预定义样式有点丑陋,主要是AbsSpinner搞鬼,下面是预定义的载入代码块(修改Spinner样式可以参考adapter这里的调用)

  public AbsSpinner(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        initAbsSpinner();

        final TypedArray a = context.obtainStyledAttributes(
                attrs, R.styleable.AbsSpinner, defStyleAttr, defStyleRes);

        final CharSequence[] entries = a.getTextArray(R.styleable.AbsSpinner_entries);
        if (entries != null) {
            final ArrayAdapter adapter = new ArrayAdapter(
                    context, R.layout.simple_spinner_item, entries);
            adapter.setDropDownViewResource(R.layout.simple_spinner_dropdown_item);
            setAdapter(adapter);
        }

        a.recycle();
    }

看完Spinner有了个大概的认知,再看AutoCompleteTextView(他的子类同理略过了),内部同样实例了ListPopuWindow,添加了一个EditText输入内容的监听

   addTextChangedListener(new MyWatcher());

如果你对这个方法不陌生应该秒懂了吧,根据输入内容,当输入内容有变化立即执行过滤匹配适配器,这里过滤具体实现暂不涉及。

CheckedTextView

在之前我写过一篇关于ListView的单选和多选相关的分析的博客http://blog.csdn.net/analyzesystem/article/details/51319841,里面有提到一个非常重要的接口Checkable,这里就不多说废话了,同时提供一个开源库关于CheckedTextView相关的扩展库(别太当真,看看就好,扩展视野)
https://github.com/akbarsha03/Custom_CheckedTextView

Chronometer

这是一个扩展子TextView的计时器控件,然而我不得不吐槽,官方提供的很多原生控件都不咋样,与开源项目同类型的开源控件没法比,关于计时器这个,在很久以前貌似我也写了一篇blog分析一个计时器开源项目做了个纪录,地址:http://blog.csdn.net/analyzesystem/article/details/50435228


小结

收获了两点:

1.在onAttachedToWindow和onDetachedFromWindow方法注册广播和取消注册,接收到广播,来做一些回调或者更新UI
2.了解了Spinner AutoCompleteTextView ListView等相关类的相关实现。

你可能感兴趣的:(Android)