Android UI开发细节Api使用技巧总结

收集平时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);
}

猜你喜欢

转载自blog.csdn.net/u010019468/article/details/78063201