您的位置:首页 > 移动开发 > Android开发

Android梳理不常用widget篇

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

TextClock

效果图



实现原理

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



在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

效果图





实现原理

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

效果图



实现原理

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

效果图



实现原理

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

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





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





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

自定义属性注释对应方法
inAnimation组件显示时用的动画setInAnimation()
outAnimation组件隐藏时用的动画setInAnimation()
animateFirstView组件显示第一个View时是否播放动画setAnimateFirstView(),默认播放动画
loopViews循环到最后一个视图是否回到第一个视图AdapterViewFlipper强制设为了true
flipInterval循环播放动画时间间隔setFlipInterval()默认1000间隔
autoStart是否自动播放动画setAutoStart()
当你使用默认播放动画切换视图感觉不是1秒,不要觉得奇怪,动画执行也需要时间的,默认in 、out 动画时常都是200。对于代码功能性调用来说我们只需要关注下面这几个方法即可



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到底有多亲密的关系?

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



他们之间的的关系到底有多深呢,且听我一一道来。最上层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<CharSequence> adapter = new ArrayAdapter<CharSequence>(
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等相关类的相关实现。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: