如何更优雅的实现标题栏

版权声明:本文为博主原创文章,未经博主允许不得转载 https://blog.csdn.net/u011692041/article/details/78500398

前言

做Android这么久了,目前见过实现标题栏的方式无非两种

1.在每一个Activity的xml中include一个共有的标题栏xml,然后在Activity的生命周期方法onCreate方法中完成标题栏的初始化
2.在每一个Activity的xml中使用自己写的标题栏控件

第二种方式明显比第一种方式好一些,因为这种方式你能在布局文件中通过自定义属性就可以完成整个标题栏的初始化,而不要在Activity中再写一些代码来初始化,并且你可以通过自定义控件的属性来控制显示成不同的样式,比较方便管理.

今天博主要介绍的这种是更为简单的一种方式。在牺牲一点性能的基础上,让标题栏更容易维护和使用

我们可以这样子使用,注解是自定义的,解析也需要自己来,这部分我都开放给用户了,我并不做任何的限制

@Title(value = "我是小金子", menuText = "菜单", menuClick = "onMenuClick")
public class MainAct extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.act_main);
    }

    public void onMenuClick(View view) {
        Toast.makeText(this, "v= " + view, Toast.LENGTH_SHORT).show();
    }

}

上面的写法有以下的好处:

  • 写法更为简单了,可以自己自定义多个注解来描述标题栏
  • 可以自定义的实现点击事件或者其他事件的回调
  • 不需要在每一个xml中引用第三方控件或者include任何的其他的xml,这个明显有优势
  • 更容易维护了,这点很重要

也有坏处:

  • 实现这个效果有一点点的反射的代码,牺牲了一点点的性能

使用步骤

使用前请先添加依赖到你的项目中,具体看底部github地址

第一步是编写一个标题栏的xml文件

xml我就不贴出来了,到时候你们自己到我的github上去clone下来看就行了,这里就贴一个xml的预览图
这里写图片描述

可以看到我写了一个很简单的普通的标题栏布局

第二步是针对xml编写一个自定义的注解

/**
 * Created by xiaojinzi on 09/11/2017 5:19 PM
 * blog: http://blog.csdn.net/u011692041
 * github: github.com/xiaojinzi123
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Title {

    /**
     * the title text of titlebar
     *
     * @return
     */
    String value() default "";

    /**
     * the back image rsd of titlebar
     */
    int backImg() default R.drawable.ehai_ui_back_icon1;

    /**
     * the menu image rsd of titlebar
     */
    int menuImg() default -1;

    /**
     * the menu text rsd of titlebar
     */
    String menuText() default "";

    /**
     * the menu click will call a method in Activity which name is this value
     *
     * @return
     */
    String menuClick() default "";

}

注解不会编写的请先自行搜索下相关的文章,不难
上面这个是楼主写的一个自定义的注解,用来描述标题栏,这些信息的值最终会被读取并且设置到标题栏的xml的View中

第三步是先在某一个Activity上使用

比如这样子

@Title(value = "我是标题", menuText = "我是菜单", menuClick = "onMenuClick")
public class MainAct extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.act_main);
    }

    public void onMenuClick(View view) {
        Toast.makeText(this, "v= " + view, Toast.LENGTH_SHORT).show();
    }

}

第四步是最重要的,就是实现标题栏的显示

要实现上述的写法,你得在每一个Activity创建的时候都能拿到Activity实例,并且你得在这个时候读取注解并且添加到Activity的上的DecorView中,原理就是我们可以在Application中通过registerActivityLifecycleCallbacks方法注册一个Activity的声明周期的回调,然后我们可以拿到所有Activity的生命周期的回调啦

小框架已经帮你实现了这个,你要做的只要实现对应的方法解析你自己的注解然后返回一个标题栏的View对象就可以了

/**
 * Created by xiaojinzi on 09/11/2017 10:51 AM
 * blog: http://blog.csdn.net/u011692041
 * github: github.com/xiaojinzi123
 */
public class App extends Application {

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

        HeadAdapter headAdapter = new HeadAdapter() {
            @Nullable
            @Override
            public View headerView(@NonNull final Activity act, @Nullable Annotation... annotations) {
                return getView(act, annotations);
            }

        };

        HeadActivityLifecycleCallbackImpl callback = new HeadActivityLifecycleCallbackImpl(new FitsSystemHeaderAdapter(headAdapter));

        registerActivityLifecycleCallbacks(callback);

    }
}

可以看到这里你需要实现一个HeadAdapter接口来返回对应的View,回调中给出了Activity和对应的注解对象集合

FitsSystemHeaderAdapter 是通过装饰者模式给你的视图添加了一个topadding,这个值正好是状态栏的高度,不需要的可以不要这个代码

然后创建一个小框架中提供的HeadActivityLifecycleCallbackImpl传入刚才的HeadAdapter,然后注册到系统中

getView方法是用户自己解析注解信息返回一个View的过程,这里给出我写的,给大家做一个参考,并且给不会写注解的人提供一个可参考的代码

    private View getView(@NonNull final Activity act, @Nullable Annotation[] annotations) {

        if (annotations == null) return null;

        boolean isHaveTitle = false;

        Title titleAnnotation = null;

        for (Annotation annotation : annotations) {
            if (annotation instanceof Title) {
                titleAnnotation = (Title) annotation;
                isHaveTitle = true;
            }
        }

        if (!isHaveTitle || titleAnnotation == null) {
            return null;
        }

        // init view

        View view = View.inflate(act.getBaseContext(), R.layout.ehai_ui_titlebar1, null);
        TextView tv_title = view.findViewById(R.id.tv_title);
        ImageView iv_back = view.findViewById(R.id.iv_back);
        ImageView iv_menu = view.findViewById(R.id.iv_menu);
        TextView tv_menu = view.findViewById(R.id.tv_menu);

        iv_back.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                act.finish();
            }
        });

        int backImg = titleAnnotation.backImg();
        if (backImg != -1) {
            iv_back.setImageResource(backImg);
        }

        String titleText = titleAnnotation.value();
        tv_title.setText(titleText);

        String menuText = titleAnnotation.menuText();
        tv_menu.setText(menuText);

        int ivMenu = titleAnnotation.menuImg();
        if (ivMenu != -1) {
            iv_menu.setImageResource(ivMenu);
        }

        // get the click method name of menu
        String menuClickMethod = titleAnnotation.menuClick();

        try {
            final Method onTitleClick = act.getClass().getMethod(menuClickMethod, new Class[]{View.class});
            View.OnClickListener listener = new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    try {
                        onTitleClick.invoke(act, v);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            };
            tv_menu.setOnClickListener(listener);
            iv_menu.setOnClickListener(listener);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }

        return view;
    }

代码中有点反射的代码,不会的同学先去了解下,不了解的可以照猫画虎的改改

完工啦,完整源码和示例都在我的github中,如何依赖使用也在我们的github中,传送门:

HeaderComponent

猜你喜欢

转载自blog.csdn.net/u011692041/article/details/78500398