收集平时UI开发中使用到或者学习笔记做个收录,好记性不如烂笔头:持续更新中。。。。
1、图片渐变技巧
第一种:叠层退去,逐渐显示底层
第二种:直接组合使用,各取一部分组成一个,利用canvas.clipRect来截取
第三种:叠层慢慢增加并且和底层取个交集:取两层绘制交集,显示上层。
PorterDuffXfermode mode = new PorterDuffXfermode(PorterDuff.Mode.SRC_IN);
mPaint.setXfermode(mode);
2、StaticLayout
android中处理文字换行的一个工具类,StaticLayout已经实现了文本绘制换行处理。其实TextView也是调用StaticLayout来实现换行的。
3、drawable.setLevel 和imageView.setImageLevel
在用来显示drawable进程的时候的 根据level值来作为进度变化,设置之后 drawable会收到onLevelChange回调通知进度变化
4、View控件的移动位置方式总结
1)改变layoutParams的with height 不建议
2)重新layout,计算位置调用view.layout() 不建议
3)调用offsetLeftAndRight(offX)和offsetTopAndBottom(offY); 建议
4)setTranslationX和setTranslationY 可以
这个也可以改变view的位置,与上面的区别是,不影响改变layoutParams中margin大小 即不改变getLeft和getRight大小,translationX就是针对getLeft大小而言的偏移量,也是一种设置与父布局大小的方式和Margin同级,但是坐标位置都是改变的,即getX =getLeft+getTranslationX
public float getX() {
return mLeft + getTranslationX();
}
上述4种均可以改变位置和点击属性
这个和移动父布局中的控件不一样,和scrollTo、scrollBy是移动ViewGroup内容,和canvas.tanslate移动一样,都是画布内容在可视布局区域内的移动,不会超过边界。而View动画-TranslationAnimation,是改变Matrix变换来是实现可视位置位移,但不改变控件的属性,如点击监听位置。
5、View的尺寸计算
1)View.post(Runnable):把计算任务post主线程队列中handlelauchActivity之后,即在界面显示之后,why?
public boolean post(Runnable action) {
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
return attachInfo.mHandler.post(action);
}
// Postpone the runnable until we know on which thread it needs to run.
// Assume that the runnable will be successfully placed after attach.
getRunQueue().post(action);
return true;
}
把Runnable任务通过handle传递到主线程之后,而此时onCreate正是发生在主线程消息队列LaunchActivity 的Message之后,即在执行完这个Message之后,UI界面已经绘制测算完成,这时候再执行Runnable这个Message既可以获得尺寸
2)onWindowFocusChange:同上,都是在完整生命周期之后,handleResumeActivity已经执行完成了
final void handleResumeActivity(IBinder token,
boolean clearHide, boolean isForward, boolean reallyResume) {
// If we are getting ready to gc after going to the background, well
// we are back active so skip it.
unscheduleGcIdler();
mSomeActivitiesChanged = true;
// TODO Push resumeArgs into the activity for consideration
ActivityClientRecord r = performResumeActivity(token, clearHide);
if (r != null) {
final Activity a = r.activity;
if (localLOGV) Slog.v(
TAG, "Resume " + r + " started activity: " +
a.mStartedActivity + ", hideForNow: " + r.hideForNow
+ ", finished: " + a.mFinished);
final int forwardBit = isForward ?
WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION : 0;
// If the window hasn't yet been added to the window manager,
// and this guy didn't finish itself or start another activity,
// then go ahead and add the window.
boolean willBeVisible = !a.mStartedActivity;
if (!willBeVisible) {
try {
willBeVisible = ActivityManagerNative.getDefault().willActivityBeVisible(
a.getActivityToken());
} catch (RemoteException e) {
}
}
if (r.window == null && !a.mFinished && willBeVisible) {
r.window = r.activity.getWindow();
View decor = r.window.getDecorView();
decor.setVisibility(View.INVISIBLE);
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = r.window.getAttributes();
a.mDecor = decor;
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
l.softInputMode |= forwardBit;
if (a.mVisibleFromClient) {
a.mWindowAdded = true;
wm.addView(decor, l);//完成测算
}
.................................
r.activity.mVisibleFromServer = true;
mNumVisibleActivities++;
if (r.activity.mVisibleFromClient) {
r.activity.makeVisible();//显示界面
}
}
if (!r.onlyLocalRequest) {
r.nextIdle = mNewActivities;
mNewActivities = r;
if (localLOGV) Slog.v(
TAG, "Scheduling idle handler for " + r);
Looper.myQueue().addIdleHandler(new Idler());
}
r.onlyLocalRequest = false;
// Tell the activity manager we have resumed.
if (reallyResume) {
try {
ActivityManagerNative.getDefault().activityResumed(token);
} catch (RemoteException ex) {
}
}
} else {
// If an exception was thrown when trying to resume, then
// just end this activity.
try {
ActivityManagerNative.getDefault()
.finishActivity(token, Activity.RESULT_CANCELED, null, false);
} catch (RemoteException ex) {
}
}
}
在addView之后ViewRootImpl会完成view的measure、layout、draw,故可以获取,而onRusmue是发生在performRusumeActivity内,在addView之前。
3 ) 、ViewTreeObserver来监听绘制、布局等事件变化
6、自定义View相关技巧和基础
两篇不错的文章收集
安卓自定义View教程目录:http://www.gcssloop.com/customview/CustomViewIndex/
Android应用自定义View绘制方法手册 :http://blog.csdn.net/yanbober/article/details/50577855
7、之前写过Android图片自动适配的文章,如果有些图片我们不希望手机进行放大适配更高的分辨率怎么办?根据《Sdk界面UI开发自动适配屏幕技巧》所说,通过density的变化来确定,那么我们指定inDensity跟TargetDensity一样大就可以了,在Resource中确实存在这样的方法:
public Drawable getDrawableForDensity(@DrawableRes int id, int density)
throws NotFoundException {
return getDrawableForDensity(id, density, null);
}
这个入参density的赋值路径:
if (value.density > 0 && value.density != TypedValue.DENSITY_NONE) {
if (value.density == density) {
value.density = metrics.densityDpi;
} else {
value.density = (value.density * metrics.densityDpi) / density;
}
}
先记录下,后面验证。getDrawableForDensity(id,getResources().getDisplayMetrics().densityDpi);因为inTargetDensity = res.getDisplayMetrics().densityDpi;两个值一样即可
8、RecyclerView.Adapter监听viewHolder是否复用,可以在复用时取消下载图片,复用肯定就是在滑动,所以优化图片加载可以
- 通过RecyclerVIew的滑动监听判断是否是滑动中,这时候通知Adapter不加载图片
- 就是复用ViewHolder时候取消下载图片
@Override
public void onBindViewHolder(Myholder holder, int position) {
String istrurl = mImgList.get(position).getImageUrl();
if (null == holder || null == istrurl || istrurl.equals("")) {
return;
}
Glide.with(mContext)
.load(istrurl)
.placeholder(R.drawable.ic_launcher_background)
.into(holder.mImvShow);
}
@Override
public void onViewRecycled(Myholder holder)
{
if (holder != null)
{
Glide.clear(holder.mImvShow);
}
super.onViewRecycled(holder);
}