GithubHelp home page GithubHelp logo

genius158 / inflaterauto Goto Github PK

View Code? Open in Web Editor NEW
82.0 5.0 15.0 275 KB

a lib that make UI look the same in different android phones, 强大的屏幕适配库(AndroidAutoLayout替代方案),不只是适配!

Java 100.00%
autolayout androidautolayout

inflaterauto's Introduction

InflaterAuto

强大的UI适配库(AndroidAutoLayout替代方案),不只是适配!
甚至可进行统一的类替换(把所有的TextView替换成ImageView)

图例

以下设计图纸为720_1280(图例分辨率分别为:1080_1920、480_800、1920_1080),布局中不属于ViewGroup的布局设置都是 采用layout_width="px",android:layout_height="px",android:layout_marginTop="px",android:paddingLeft="px"具体px值设置
(ps:只适配px)

screen1080_1920 screen480_800
screen1920_1080

概述

本库实现,在view生成时直接调整内部相关属性,宽度高度等需要父类调整的,则替换原本的viewgroup为可适配的viewgroup
是的,本库,可以统一对你想要更改的view全部替换,完成很多其他的事情,所以这不仅仅只是一个适配库

选择切入点

view的设置LayoutParams是在LayoutInflater的rInflate方法中执行的
void rInflate(XmlPullParser parser, View parent, Context context,
           AttributeSet attrs, boolean finishInflate) throws XmlPullParserException, IOException {
    ...
    final View view = createViewFromTag(parent, name, context, attrs);
    final ViewGroup viewGroup = (ViewGroup) parent;
    final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
    rInflateChildren(parser, view, attrs, true);//这里是递归调继续创建View
    viewGroup.addView(view, params);
    ...
    }

可以看到,LayoutParams是在这里创建的,这个方法是我们最需要更改操作的,然而我们并不能覆写这个方法,AndroidAutoLayout有一系列的Auto开头的ViewGroup ,其重写的也就是generateLayoutParams,直接返回调整过的params,然而它仍然需要在OnMeasure的时候对所有子View内部相关属性做调整,为了提升效率, 2.x不在返回整个View后递归调整,而是,采用View自身的属性,在View生成后直接调整,LayoutParams在父类的生成后直接调整,可调整LayoutParams的父类配置注解,在编译时自动生成。

gradle

implementation 'com.yan:inflaterauto:2.0.17'
annotationProcessor 'com.yan:inflaterauto-compiler:2.0.17'//如果你不需要自动生成适配类的功能,不需要引入

使用

// application 初始化
public class InflaterAutoApp extends Application {

    @Override
    public void onCreate() {
        super.onCreate();

        /*
         * 以下可以写在任何地方,只要在生成View之前
         */
        InflaterAuto.init(new InflaterAuto.Builder()
            .width(720)
            .height(1280)
            .baseOnDirection(InflaterAuto.BaseOn.Both)// 宽度根据宽度比例缩放,长度根据长度比例缩放
            // 由 com.yan.inflaterautotest.InflaterConvert 编译生成,自动添加前缀InfAuto
            // 你也可以添加你自己的实现AutoConvert的类,替换任何一种view成为你想替换的view
            .inflaterConvert(new InfAutoInflaterConvert())
            .build()
        );
    }


    /**
     * 如果你使用了LayoutInflater.from(getApplicationContext())或者LayoutInflater.from(getApplication())
     * 就需要以下操作,如果没有,以下方法不必重写
     */
   @Override
    protected void attachBaseContext(Context base) {
        //替换Inflater
        super.attachBaseContext(InflaterAuto.wrap(base));
    }
}

// activity 重写attachBaseContext
public class MainActivity extends AppCompatActivity {
    @Override
    protected void attachBaseContext(Context base) {
        //替换Inflater
        super.attachBaseContext(InflaterAuto.wrap(base));
    }
}

// 注解设置,add 你用到的ViewGroup
@Convert({LinearLayout.class
        , FrameLayout.class
        , NestedScrollView.class
        , RecyclerView.class
        , ListView.class
        , ScrollView.class
        , CoordinatorLayout.class
        , ConstraintLayout.class
        , AutoLayout.class
} )
public class InflaterConvert implements AutoConvert {// 类名随便写
 @Override
    public HashMap<String, String> getConvertMap() {
        return null;// 添加映射
    }
}

说明

view的适配,不包括maxHeight、maxWidth,因为其中涉及反射,影响效率,暂时不打算处理,同时用的其实也很少~

类型转换接口

public interface AutoConvert {
    HashMap<String, String> getConvertMap();
}

如果默认的适配效果满足不了需求,或者你想要的不只是适配功能,你可以自己实现该接口,Hashmap kay为你要替换的view在布局文件中标签的名字,value为替换后的类

例如 动态更新皮肤,你可以重写相关的view,并替换,给它添加一个广播监听,需要换肤的时候,发出广播,然后你重写的view接受到广播 后就可以做相关操作。
是的有了替换view的功能,你可以为所欲为!
(ps:Hashmap Key是根据xml里的标签名称对应的,比如LinearLayout没有包名,support包下的是全类名)



自动编译生成的 InfAutoInflaterConvert
public class InfAutoInflaterConvert extends InflaterConvert implements AutoConvert {
    public HashMap<String, String> getConvertMap() {
        //自己写的InflaterConvert可以先配置映射,这里会把先设置的映射添加进来
        HashMap<String, String> classMap = new HashMap();
        HashMap<String, String> superMap = super.getConvertMap();
        if(superMap != null) {
            classMap.putAll(superMap);
        }

        classMap.put("android.support.v7.widget.RecyclerView", "com.yan.inflaterautotest.InfAutoRecyclerView");
        classMap.put("android.support.design.widget.CoordinatorLayout", "com.yan.inflaterautotest.InfAutoCoordinatorLayout");
        classMap.put("com.yan.inflaterautotest.AutoLayout", "com.yan.inflaterautotest.InfAutoAutoLayout");
        classMap.put("ListView", "com.yan.inflaterautotest.InfAutoListView");
        classMap.put("ScrollView", "com.yan.inflaterautotest.InfAutoScrollView");
        classMap.put("FrameLayout", "com.yan.inflaterautotest.InfAutoFrameLayout");
        classMap.put("android.support.v4.widget.NestedScrollView", "com.yan.inflaterautotest.InfAutoNestedScrollView");
        classMap.put("android.support.constraint.ConstraintLayout", "com.yan.inflaterautotest.InfAutoConstraintLayout");
        classMap.put("LinearLayout", "com.yan.inflaterautotest.InfAutoLinearLayout");
        return classMap;
    }
}



自动编译生成的 InfAutoFrameLayout
public class InfAutoFrameLayout extends FrameLayout {
   ...
   // 构造函数
   ...
    public LayoutParams generateLayoutParams(AttributeSet attrs) {
        LayoutParams vlp = super.generateLayoutParams(attrs);// 拿到LayoutParams
        AutoUtils.autoLayout(vlp, this.getContext(), attrs);// 适配LayoutParams
        return vlp;
    }
}

鸣谢

hongyangAndroid/AndroidAutoLayout
chrisjenx/Calligraphy

inflaterauto's People

Contributors

genius158 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

inflaterauto's Issues

耳机广播监听 按耳机线的按钮暂停 就会报java.lang.ClassCastException: com.yan.inflaterauto.AutoContextWrapper cannot be cast to android.app.ContextImpl

java.lang.RuntimeException: Unable to start receiver com.weile.graph.receiver.PhoneReceiver: java.lang.ClassCastException: com.yan.inflaterauto.AutoContextWrapper cannot be cast to android.app.ContextImpl
at android.app.ActivityThread.handleReceiver(ActivityThread.java:3119)
at android.app.ActivityThread.-wrap18(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1627)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:165)
at android.app.ActivityThread.main(ActivityThread.java:6365)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:883)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:773)
Caused by: java.lang.ClassCastException: com.yan.inflaterauto.AutoContextWrapper cannot be cast to android.app.ContextImpl
at android.app.ActivityThread.handleReceiver(ActivityThread.java:3109)
at android.app.ActivityThread.-wrap18(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1627)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:165)
at android.app.ActivityThread.main(ActivityThread.java:6365)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:883)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:773)
07-13 19:52:46.947 6834-6834/com.weile.graph E/CustomActivityOnCrash: App has crashed, executing CustomActivityOnCrash's UncaughtExceptionHandler
java.lang.RuntimeException: Unable to start receiver com.weile.graph.receiver.PhoneReceiver: java.lang.ClassCastException: com.yan.inflaterauto.AutoContextWrapper cannot be cast to android.app.ContextImpl
at android.app.ActivityThread.handleReceiver(ActivityThread.java:3119)
at android.app.ActivityThread.-wrap18(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1627)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:165)
at android.app.ActivityThread.main(ActivityThread.java:6365)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:883)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:773)
Caused by: java.lang.ClassCastException: com.yan.inflaterauto.AutoContextWrapper cannot be cast to android.app.ContextImpl
at android.app.ActivityThread.handleReceiver(ActivityThread.java:3109)
at android.app.ActivityThread.-wrap18(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1627)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:165)
at android.app.ActivityThread.main(ActivityThread.java:6365)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:883)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:773)

@Convert这个是不是没传.

annotationProcessor 'com.yan:inflaterauto-compiler:2.0.11'
implementation 'com.yan:inflaterauto:2.0.11'

@Conver这个注解没有 也就是 Convert.java 这个文件没包含进来啊

hello,你的这个思路非常棒,我这有一个补充。如果有部分属性不需要适配怎么办?下面是我想出来的一个解决方案。

这个需要把不需要适配的属性设置ContentDescription并且用;分开即可

package com.yan.inflaterauto;

import android.support.v7.widget.RecyclerView;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.TextView;

import java.util.Arrays;
import java.util.Set;
import java.util.HashSet;

/**
 * Created by yan on 25/11/2017
 */
public class AutoUtils {
    public static View auto(View view) {
        if (InflaterAuto.getInstance() == null) {
            return view;
        }
        innerAuto(view);
        return view;
    }

    private static void innerAuto(View view) {
        if (view == null) {
            return;
        }

        Set<String> notAutoAttr = null;
        Object o = view.getContentDescription();
        if (o != null && o instanceof String) {
            notAutoAttr = new HashSet<>();
            String noAutoAttrAllStr = (String) o;
            String[] noAutoAttrStrs = noAutoAttrAllStr.split(";");
            notAutoAttr.addAll(Arrays.asList(noAutoAttrStrs));
        }

        InflaterAuto inflaterAuto = InflaterAuto.getInstance();
        final float hRatio = inflaterAuto.getHRatio();
        final float vRatio = inflaterAuto.getVRatio();

        if (inflaterAuto.except(view.getClass())) {
            final ViewGroup.LayoutParams lp = view.getLayoutParams();
            if (lp != null) {
                if (needAuto(notAutoAttr, "width")) {
                    if (lp.width != -1 && lp.width != -2) {
                        lp.width = (int) (lp.width * hRatio + 0.5);
                    }
                }

                if (needAuto(notAutoAttr, "height")) {
                    if (lp.height != -1 && lp.height != -2) {
                        lp.height = (int) (lp.height * vRatio + 0.5);
                    }
                }


                if (lp instanceof ViewGroup.MarginLayoutParams) {
                    final ViewGroup.MarginLayoutParams mplp = (ViewGroup.MarginLayoutParams) lp;
                    if (needAuto(notAutoAttr, "leftMargin")) {
                        mplp.leftMargin = (int) (mplp.leftMargin * hRatio + 0.5);
                    }
                    if (needAuto(notAutoAttr, "rightMargin")) {
                        mplp.rightMargin = (int) (mplp.rightMargin * hRatio + 0.5);
                    }
                    if (needAuto(notAutoAttr, "topMargin")) {
                        mplp.topMargin = (int) (mplp.topMargin * vRatio + 0.5);
                    }
                    if (needAuto(notAutoAttr, "bottomMargin")) {
                        mplp.bottomMargin = (int) (mplp.bottomMargin * vRatio + 0.5);
                    }

                }
            }

            if (view instanceof TextView) {
                if (needAuto(notAutoAttr, "textSize")) {
                    final TextView tv = (TextView) view;
                    final float textSize = tv.getTextSize() * Math.min(hRatio, vRatio);
                    tv.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
                }
            }
            final int paddingLeft = needAuto(notAutoAttr, "paddingLeft") ? (int) (view.getPaddingLeft() * hRatio +
                    0.5) :
                    view.getPaddingLeft();
            final int paddingTop = needAuto(notAutoAttr, "paddingTop") ? (int) (view.getPaddingTop() * vRatio + 0.5)
                    : view.getPaddingTop();
            final int paddingRight = needAuto(notAutoAttr, "paddingRight") ? (int) (view.getPaddingRight() * hRatio +
                    0.5) : view.getPaddingRight();
            final int paddingBottom = needAuto(notAutoAttr, "paddingBottom") ? (int) (view.getPaddingBottom() * vRatio
                    + 0.5) : view.getPaddingBottom();

            view.setPadding(paddingLeft
                    , paddingTop
                    , paddingRight
                    , paddingBottom
            );

            if ((view instanceof ViewGroup) && !(view instanceof AbsListView) && !(view instanceof RecyclerView)) {
                for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {
                    innerAuto(((ViewGroup) view).getChildAt(i));
                }
            }
        }
    }

    public static float getValueHorizontal(float value) {
        return InflaterAuto.getInstance().getHRatio() * value;
    }

    public static float getValueVertical(float value) {
        return InflaterAuto.getInstance().getVRatio() * value;
    }

    /**
     * 是否需要自动布局
     *
     * @param noAutolist 不需要自动布局的属性集合
     * @param nowAttr    当前的属性名称
     * @return
     */
    public static boolean needAuto(Set<String> noAutolist, String nowAttr) {
        return noAutolist == null || !noAutolist.contains(nowAttr);
    }
}

关于padding和margin的值是dp的问题

我在使用Snackbar的过程中发现snackbar的padding没了,然后看您的源码发现是只对px的值做了计算,而dp的值就直接continue了,这样在使用snackbar或者一些第三方库的时候显示会很不友好

安装一直报错

SUPPORTED_64_BIT_ABIS=[Ljava.lang.String;@2b39a594
versionCode=1
BOARD=msm8952
BOOTLOADER=unknown
TYPE=user
ID=LMY47V
TIME=1514451351000
BRAND=vivo
TAG=Build
SERIAL=e2e9b2c3
HARDWARE=qcom
SUPPORTED_ABIS=[Ljava.lang.String;@1c76c83d
CPU_ABI=arm64-v8a
RADIO=M8939.1020.3
IS_DEBUGGABLE=false
MANUFACTURER=vivo
SUPPORTED_32_BIT_ABIS=[Ljava.lang.String;@7de17a6
TAGS=release-keys
CPU_ABI2=
UNKNOWN=unknown
USER=compiler
FINGERPRINT=vivo/PD1602/PD1602:5.1.1/LMY47V/compiler12281653:user/release-keys
HOST=compiler025
PRODUCT=PD1602
versionName=1.0
DISPLAY=LMY47V release-keys
MODEL=vivo X7
DEVICE=PD1602
android.view.InflateException: Binary XML file line #21: Error inflating class LinearLayout
at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:802)
at android.view.LayoutInflater.inflate(LayoutInflater.java:488)
at com.c.a.e.inflate(Unknown Source)
at android.view.LayoutInflater.inflate(LayoutInflater.java:420)
at android.view.LayoutInflater.inflate(LayoutInflater.java:371)
at android.widget.Toast.makeText(Toast.java:276)
at com.banquanjia.dci.e.b$1.run(Unknown Source)
Caused by: java.lang.ClassNotFoundException: Didn't find class "android.view.com.banquanjia.dci.autolayout.InfAutoLinearLayout" on path: DexPathList[[zip file "/data/app/com.banquanjia.dci-1/base.apk"],nativeLibraryDirectories=[/vendor/lib64, /system/lib64]]
at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56)
at java.lang.ClassLoader.loadClass(ClassLoader.java:511)
at java.lang.ClassLoader.loadClass(ClassLoader.java:469)
at android.view.LayoutInflater.createView(LayoutInflater.java:577)
at android.view.LayoutInflater.onCreateView(LayoutInflater.java:710)
at com.c.a.e.onCreateView(Unknown Source)
at android.view.LayoutInflater.onCreateView(LayoutInflater.java:727)
at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:786)
... 6 more
Suppressed: java.lang.ClassNotFoundException: android.view.com.banquanjia.dci.autolayout.InfAutoLinearLayout
at java.lang.Class.classForName(Native Method)
at java.lang.BootClassLoader.findClass(ClassLoader.java:781)
at java.lang.BootClassLoader.loadClass(ClassLoader.java:841)
at java.lang.ClassLoader.loadClass(ClassLoader.java:504)
... 12 more
Caused by: java.lang.NoClassDefFoundError: Class not found using the boot class loader; no stack available
java.lang.ClassNotFoundException: Didn't find class "android.view.com.banquanjia.dci.autolayout.InfAutoLinearLayout" on path: DexPathList[[zip file "/data/app/com.banquanjia.dci-1/base.apk"],nativeLibraryDirectories=[/vendor/lib64, /system/lib64]]
at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56)
at java.lang.ClassLoader.loadClass(ClassLoader.java:511)
at java.lang.ClassLoader.loadClass(ClassLoader.java:469)
at android.view.LayoutInflater.createView(LayoutInflater.java:577)
at android.view.LayoutInflater.onCreateView(LayoutInflater.java:710)
at com.c.a.e.onCreateView(Unknown Source)
at android.view.LayoutInflater.onCreateView(LayoutInflater.java:727)
at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:786)
at android.view.LayoutInflater.inflate(LayoutInflater.java:488)
at com.c.a.e.inflate(Unknown Source)
at android.view.LayoutInflater.inflate(LayoutInflater.java:420)
at android.view.LayoutInflater.inflate(LayoutInflater.java:371)
at android.widget.Toast.makeText(Toast.java:276)
at com.banquanjia.dci.e.b$1.run(Unknown Source)
Suppressed: java.lang.ClassNotFoundException: android.view.com.banquanjia.dci.autolayout.InfAutoLinearLayout
at java.lang.Class.classForName(Native Method)
at java.lang.BootClassLoader.findClass(ClassLoader.java:781)
at java.lang.BootClassLoader.loadClass(ClassLoader.java:841)
at java.lang.ClassLoader.loadClass(ClassLoader.java:504)
... 12 more
Caused by: java.lang.NoClassDefFoundError: Class not found using the boot class loader; no stack available

关于使用dialog的时候发生的错误

在使用dialog的时候,在dialog.setContentView这里会报错,我不确定是我的inflate方法用的不对还是setContentView用的不对,所以希望您能出一个dialog的使用demo

在webView中使用有问题

当和webView中的html5交互时,比如弹出输入框,由于
/**
* 屏幕自适应
* @param base
*/
@OverRide
protected void attachBaseContext(Context base) {
super.attachBaseContext(InflaterAuto.wrap(base));
}
导致闪退

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.