android 小知识

狂风中的少年 提交于 2020-03-08 00:15:17

1.Message 创建应该使用obtain()方法,而不应该使用 new

当我们调用Message.obtain()方法时,如果复用池中存在Message对象,我们就不会去创建一个新的Message对象。这样就避免了频繁创建和销毁Messgae对象的带来的性能开销。

2.在电脑上拦截模拟器请求

1.首先先设置号模拟器上的代理模式,WIFI里面有个修改网络,里面有个代理,打开后选择打开
代理服务器主机名填你使用的电脑IP地址,代理服务端口填你要监听的app连接后台的端口保存就好了

3.Android 下拉通知栏时Activity的生命周期

下拉通知栏对生命周期是没有什么影响的

4.Activity的四种启动模式launchMode的区别

  1. standard
    standard模式是默认的启动模式,不用为配置android:launchMode属性即可,当然也可以指定值为standard。

  2. singleTop
    在上面的基础上为指定属性android:launchMode=“singleTop”,系统就会按照singleTop启动模式处理跳转行为。singleTop启动模式,如果发现有对应的Activity实例正位于栈顶,则重复利用,不再生成新的实例。

  3. singleTask
    在上面的基础上修改FActivity的属性android:launchMode=“singleTask”。singleTask模式,如果发现有对应的Activity实例,则使此Activity实例之上的其他Activity实例统统出栈,使此Activity实例成为栈顶对象,显示到幕前。

  4. singleInstance
    这种启动模式比较特殊,因为它会启用一个新的栈结构,将Acitvity放置于这个新的栈结构中,并保证不再有其他Activity实例进入。

5.Activity 状态保存与恢复

有如下几种情况是需要对数据进行保存的

  1. 点击了返回键
  2. 锁屏
  3. 点击了Home键
  4. 有其他APP进入了前台(比如接电话)
  5. 启动新的Activity
  6. 屏幕发生了旋转
  7. App发生意外被杀死

首先是在onSaveInstanceState中保存数据
紧接着在onRestoreInstanceState对数据进行恢复

6.scrollview 加ListView组合嵌套 ListView只会出现一个条目

修改方法:重新测量ListView的高度

public void setListViewHeightBasedOnChildren(ListView listView) {
        // 获取ListView对应的Adapter
        android.widget.ListAdapter adapter = listView.getAdapter();
        if (adapter == null) {
            return;
        }

        int totalHeight = 0;
        for (int i = 0, len = adapter.getCount(); i < len; i++) {
            // listAdapter.getCount()返回数据项的数目
            View listItem = adapter.getView(i, null, listView);
            // 计算子项View 的宽高
            listItem.measure(0, 0);
            // 统计所有子项的总高度
            totalHeight += listItem.getMeasuredHeight();
        }
        ViewGroup.LayoutParams params = listView.getLayoutParams();
        params.height = totalHeight+ (listView.getDividerHeight() * (adapter.getCount() - 1));
        // listView.getDividerHeight()获取子项间分隔符占用的高度
        // params.height最后得到整个ListView完整显示需要的高度
        listView.setLayoutParams(params);
    }

7.Android事件分发机制

android的事件分发机制:开始是有我们点击手机屏幕的动作

动作 作用
MotionEvent.ACTION_DOWN 手指在手机屏幕按下
MotionEvent.ACTION_MOVE 手指在手机屏幕移动
MotionEvent.ACTION_UP 手指在手机屏幕抬起来
MotionEvent.ACTION_CANCEL 因为异常原因,而退出当前操作的

image

手指按下屏幕后 事件最先进入的是Activity里面执行 dispatchTouchEvent方法

接着Activity通过dispatchTouchEvent方法把事件分发给ViewGroup层 的dispatchTouchEvent方法

ViewGroup层的dispatchTouchEvent方法有三个走向,return true—事件终结 return false—事件回溯给父类的onTouchEvent

还有一个调用super 就是调用 把事件给ViewGroup层的独有的onInterceptTouchEvent方法

就是问一问这个事件要不要留给自己来处理,如果return true的话 它就会把这个事件交给自己的onTouchEvent来处理, 如果return false 的话它就会往子控件那里面传。

传到子View后就遍历循环所有子View 如果没有的话就是回溯给父类,如果找到子View的话就消费

如果都没有的话就是一直往父类回溯,所以这个图大概是呈U形状的结构

最后总结一下 对于 dispatchTouchEvent,onTouchEvent,return true是终结事件传递。return false 是回溯到父View的onTouchEvent方法。

ViewGroup 想把自己分发给自己的onTouchEvent,需要拦截器onInterceptTouchEvent方法
return true 把事件拦截下来。

ViewGroup 的拦截器onInterceptTouchEvent 默认是不拦截的,所以return super.onInterceptTouchEvent()=return false;

View 没有拦截器,为了让View可以把事件分发给自己的onTouchEvent,View的dispatchTouchEvent默认实现(super)就是把事件分发给自己的onTouchEvent。

关于事件分发机制里面拦截点击事件的方法

public class MyView extends LinearLayout{
    public MyView(Context context) {
        super(context);
    }

    public MyView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        //返回true的话就是拦截事件不给这个布局下面的控件消费,返回false的话就是不给布局消费,直接传递给布局下面的控件消费
        //在哪个控件的坐标上就给哪个控件消费
        //这个方法是viewgroup独有的方法
        //事件分发机制三层 activity--》viewGroup--》view
        return true;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        return super.onTouchEvent(event);
    }
}

继承的基类是ViewGroup 只有ViewGroup才有那个拦截点击事件的方法,因为布局也是继承ViewGroup的 所以也可以继承一些布局


还有解决滑动冲突的时候也是这么解决的,重写ViewPage 毕竟ViewPage也是继承ViewGroup的,所以我们可以根据当前手指滑动的距离来判断使用者需要进行哪个滑动

8.Android 加载大图时造成的内存问题

要想加载大图的时候不会造成OOM

BitmapFactory这个类提供了多个解析方法(decodeByteArray, decodeFile, decodeResource等)用于创建Bitmap对象,我们应该根据图片的来源选择合适的方法。比如SD卡中的图片可以使用decodeFile方法,网络上的图片可以使用decodeStream方法,资源文件中的图片可以使用decodeResource方法。


上面提到的方法都会自动的为我们的Bitmap对象分配内存,使用

BitmapFactory.Options options = new BitmapFactory.Options();

创建一个对象 再用options可以调用options.inJustDecodeBounds = true;就会禁止为我们的Bitmap分配内存了

BitmapFactory.decodeResource(getResources(), R.id.myimage, options);

这样options里面就会有图片的大小等数据了 再根据控件的大小 我们可以知道我们要加载的这个图片会不会比这个控件大,来确定加载的时候要不要压缩图片
如果图片比控件大很多,这时候我们就需要把图片压缩。

压缩的话有一个值需要注意 inSampleSize 这个是一个压缩比例 ,就是要比原来的比例变小几倍,这个数字可以通过原来的图片大小和控件大小的比例来算出

public static int calculateInSampleSize(BitmapFactory.Options options,
                                            int reqWidth(控件宽度), int reqHeight(控件高度)) {
        // 源图片的高度和宽度
        final int height = options.outHeight;
        final int width = options.outWidth;
        int inSampleSize = 1;
        if (height > reqHeight || width > reqWidth) {
            // 计算出实际宽高和目标宽高的比率
            final int heightRatio = Math.round((float) height / (float) reqHeight);
            final int widthRatio = Math.round((float) width / (float) reqWidth);
            // 选择宽和高中最小的比率作为inSampleSize的值,这样可以保证最终图片的宽和高
            // 一定都会大于等于目标的宽和高。
            inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
        }
        return inSampleSize;
    }

根据得出的inSampleSize

    public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
                                                         int reqWidth, int reqHeight) {
        // 第一次解析将inJustDecodeBounds设置为true,来获取图片大小
            final BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeResource(res, resId, options);
        // 调用定义的方法计算inSampleSize值
        options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
        // 使用获取到的inSampleSize值再次解析图片
        options.inJustDecodeBounds = false;
        return BitmapFactory.decodeResource(res, resId, options);
    }

我们就能得出一个Bitmap的图片对象我们再调用就设置好了图片

imgShowImg.setImageBitmap(decodeSampledBitmapFromResource(getResources(),R.drawable.test,100,100));

9.用getVisibility() 判断用户是否能看见并不好

getVisibility()只判断它自身是否是显示状态。但是如果它的父级不可见呢?
用 isShown() 方法更合适些,会先判断当前 View 的flag, 然后循环拿到父View,判断是不是可见。只要有一个是不可见的,就返回false。

10.Android自定义View(继承View)

构造方法

  1. 这个一般是在没有在布局文件里面声明组件的,都是在.java文件中new出来的对象使用的构造方法
    public ProgressCustom(Context context) {
        super(context);
    }
  1. 这个是布局文件有声明过的组件,在,java中findViewById后会使用的构造方法
    public ProgressCustom(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

这个构造方法可以在xml布局文件中写入自己想要的属性只要在res文件中创建一个attr的资源文件夹,里面可以创建一些你想要在自己控件内使用的属性


<?xml version="1.0" encoding="utf-8"?>
<resources>
 
    <declare-styleable name="Test">
        <attr name="test1" format="string" />
        <attr name="test2" format="integer" />
        <attr name="test3" format="reference" />
    </declare-styleable>
 
</resources>

其中attr标签就是我们自定义的属性了,name是属性名称,format是该属性可以取值的类型。
最后就可以在XML文件使用这些自定义属性,并且在代码中拿到了。当然,给属性取名字的时候最好不要跟一些控件的属性名字相同。

TypedArray array=context.obtainStyledAttributes(R.styleable.Test);
        String str = array.getColor(R.styleable.Test_test1, "123");
        array.recycle();

这样的话我们在上面得到了布局页面设置属性的值,这样就可以给我们的控件设置属性了。

onMeasure()

重写View里面的 onMeasure 方法的话,一般要对控件的模式进行判断,不然在我们设置控件为wrap_content的话会跟match_parent的效果一样

模式 作用
MeasureSpec.AT_MOST 这个是相当于布局文件里面的wrap_content
MeasureSpec.EXACTLY 这个相当于布局文件里面的match_parent和一些固定的大小值
MeasureSpec.UNSPECIFIED 要多大有多大(书上是这样写的)
onDraw()

重写里面的onDraw()方法,一般对象的初始化不要放在这个方法里面,因为在绘制UI的时候,这个方法会被调用很多次。

动态刷新自定义控件UI 可以调用postDelayed来刷新

getHandler().postDelayed(runnable, intervalTime(间隔时间));//间隔一段时间再进行重绘
private Runnable runnable = new Runnable() {
        @Override
        public void run() {
            (你要干的事)
            invalidate();//重新绘制
        }
    };
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!