闲得蛋疼,折腾折腾android源码包下面widget包下面很少用以及几乎没听说过的控件(针对自己而言,莫要批斗(⊙o⊙)哦丫)。
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控件继承自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内部就一个核心点,设置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没用过的可以去了解一下)
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
这个ImageView的扩展可以简单的了解一下,推荐农民伯伯大神博客地址:http://www.cnblogs.com/over140/archive/2010/09/28/1837287.html,开发中用的比较少,大概看看就行了
ViewFlipper、ViewSwitcher都是继承自ViewAnimator,ViewFlipper与AdapterViewFlipper差别不大略过,这里主要是关注一下ViewSwitcher相关类扩展一下视野。ViewAnimator是继承自FrameLayout,而ViewSwitcher是其子类,强制有且只能有2个childView,TextSwitcher限定childView必须是TextView.关于这类扩展控件自行检索Github.
直接贴代码估计比较晦涩难懂了,能把人给绕晕了,我们还是先看图说话吧。
他们之间的的关系到底有多深呢,且听我一一道来。最上层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());
如果你对这个方法不陌生应该秒懂了吧,根据输入内容,当输入内容有变化立即执行过滤匹配适配器,这里过滤具体实现暂不涉及。
在之前我写过一篇关于ListView的单选和多选相关的分析的博客http://blog.csdn.net/analyzesystem/article/details/51319841,里面有提到一个非常重要的接口Checkable,这里就不多说废话了,同时提供一个开源库关于CheckedTextView相关的扩展库(别太当真,看看就好,扩展视野)
https://github.com/akbarsha03/Custom_CheckedTextView
这是一个扩展子TextView的计时器控件,然而我不得不吐槽,官方提供的很多原生控件都不咋样,与开源项目同类型的开源控件没法比,关于计时器这个,在很久以前貌似我也写了一篇blog分析一个计时器开源项目做了个纪录,地址:http://blog.csdn.net/analyzesystem/article/details/50435228
收获了两点:
1.在onAttachedToWindow和onDetachedFromWindow方法注册广播和取消注册,接收到广播,来做一些回调或者更新UI
2.了解了Spinner AutoCompleteTextView ListView等相关类的相关实现。