方案一:使用Android库提供的组件做布局优化
1.使用 <include/> 标签复用我们的布局
通过layout属性来引入我们制定要使用的布局文件,优点呢就是,复用布局
<include
android:id="@+id/layoutId"
layout="@layout/layout"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
如果我们给include所加载的layout布局的根容器设置了id属性,也在include标签中设置了id属性,同时需要在代码中获取根容器的控件对象时,一定要将这两个id设置相同的名称!否则,将获取不到根容器对象,即为null,就是说我们实例化include加载的布局为null。
2.使用<ViewStub/>这个标签懒加载
我们经常会遇到这样的情况,运行时动态根据条件来决定显示哪个View或布局。常用的做法是把View都写在上面,先把它们的可见性都设为View.GONE,然后在代码中动态的更改它的可见性。这样的做法的优点是逻辑简单而且控制起来比较灵活。但是它的缺点就是,耗费资源。虽然把View的初始可见View.GONE但是在Inflate布局的时候View仍然会被Inflate,也就是说仍然会创建对象,会被实例化,会被设置属性。也就是说,会耗费内存等资源。
<ViewStub/>是一个轻量级的View,它一个看不见的,默认不占布局位置,在Inflate布局的时候,只有ViewStub会被初始化。在要显示的时候调用 ViewStub本有的属性
viewStub.setVisibility(View.VISIBLE);//显示
viewStub.inflate();//显示
3.使用<Merge/>标签 减少布局层级
merge主要是进行UI布局的优化的,删除多余的层级,<merge/>多用于替换FrameLayout或者当一个布局包含另一个布局的时候,<merge/>标签用于消除师徒层次结构中多余的视图组。例如你的朱布局文件是垂直的,此时如果你引入一个垂直布局的<include>.这时如果include布局使用的LinearLayout就没意义了,使用的话反而减慢你的UI表现。这时可以使用<merge/>标签优化。<merge>标签也就是排除一个布局插入另一个布局产生的多余的viewgroup.,当布局根布局没有实际属性仅仅起的是一个简单的父viewgroup时(比如我们的LinerLayout 等),可以用merge来代替,merge不会作为一个层级进行绘制,<merge />只能作为XML布局的根标签使用。当Inflate以<merge />开头的布局文件时,必须指定一个父ViewGroup,并且必须设定attachToRoot为true,(attachToRoot:是否将root附加到布局文件的根视图上 )其<merge />其的大小是有它的子View决定的。
方案二:使用强软弱虚来控制内存
强引用的特点:当内存不足, jvm开始垃圾回收,对于强引用的对象,就算出现OOM异常也不会对该对象进行回收,Android内存泄露大部分都是强引用导致的,通过new关键字引用的。
软引用特点:软引用是相对强引用弱点的引用,需要使用SoftReference类来实现,当系统内存充足时,它不会被回收,当系统内存不足时,它会被回收,软引用通常使用在对内存比较敏感时使用,也可以使用软应用来解决我们的内存泄露问题。
弱引用特点:弱引用需要用WeakReference来实现,它比软引用的生存期更短.不管内存是否够用,只要gc扫描到它都会被回收
虚引用特点:虚引用是使用PhantomReference类实现,做了这么多年开发 没用到过,不知道它的使用场景在哪里,如果一个对象仅持有虚引用,那么它就和没任何引用一样,在任何时候都有可能被垃圾回收, 它不能单独使用也不能通过它访问对象,虚引用必须配合ReferenceQueue一起使用,虚引用的作用主要是跟踪对象被垃圾回收的状态,
ReferenceQueue:这个类的作用就是对象被垃圾回收后会存放到引用队列中。
方案三:百分比适配屏幕的方案
适配方案一:
这个方案思想是我按照自己的理解写的,虽然能百分百适配但是可能也有一些小的细节没有想到,简单举个例子仅供参考!
首先呢,要用到的技术如下
其一 是需要我们的注解
注解这里的知识点呢,有原注解和自定义注解,那么我们的注解都是在原注解的基础上写的注解
主要解释两个我们常用的注解,代码如下:
//使用两个原注解
@Retention(RetentionPolicy.RUNTIME) // 什么时候运行呢?RUNTIME这个字段就是运行时加载 节省资源
@Target(ElementType.FIELD)//作用到哪里?FIELD这个字段就是作用到我们的字段上
public @interface AutoSize {
//接收的参数 可以接收 int String Class
int id();//接收组件id
int width();//接收宽的比例
int hight();//接收高的比例
}
其二 是我们的反射技术
有了注解帮我们接收的参数我们就可以在运行前做一些我们想要的操作了,但是拿到了 Class String int 这些参数怎么能实现 我们要的功能呢,那么这时候就需要我们技术反射了。代码如下:
public class MainActivity extends AppCompatActivity {
@AutoSize(width = 2,hight = 2,id=R.id.btn_one)//传入比例
Button button;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
AutoUtils.bind(this,R.layout.activity_main3);
}
}
/**
* 按比例对我们的View进行缩放
*/
public class AutoUtils {
private static int mLayoutId;
public static void bind(Activity activity,int layoutId){
activity.setContentView(layoutId);
//通过本类的Activity获得Activity视图中的子View这样我们就可以控制他的宽高了
bindView(activity);
}
private static void bindView(Activity activity){
Class<? extends Activity> aClass = activity.getClass();
Field[] declaredFields = aClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
//如果我们的字段有注解那么就返回,没有的话就返回null,就是判断有没有注解
AutoSize annotation = declaredField.getAnnotation(AutoSize.class);
if (annotation!=null){
//获得屏幕的宽高
Display defaultDisplay = activity.getWindow().getWindowManager().getDefaultDisplay();
int width = defaultDisplay.getWidth();
int height = defaultDisplay.getHeight()-getStatusBarHeight(activity);
Log.d("msg","状态栏高度"+getStatusBarHeight(activity));
//annotation是我们的注解,可以拿到我们通过注解传进来的值
LinearLayout.LayoutParams params=new LinearLayout.LayoutParams(
width/annotation.width(),height/annotation.hight());
View mView = activity.findViewById(annotation.id());//通过传进来的activity调用查找我们的View
mView.setLayoutParams(params);
}
}
}
/**
* 获取状态栏高度
* @param context
* @return
*/
public static int getStatusBarHeight(Context context) {
Resources resources = context.getResources();
int resourceId = resources.getIdentifier("status_bar_height", "dimen", "android");
int height = resources.getDimensionPixelSize(resourceId);
return height;
}
没有缩放前,也就是我们的宽高都是wrap_content的时候
缩放后,代码是按照屏幕二分之一的比例改变的View大小