手把手教你打造通用AppBar(附Demo)

打造通用AppBar,解放你的时间.

原因

相信不少小伙伴在开发过程中都要用到ToolBar.但是SDK提供的ToolBar在很大程度上无法达到理想的扩展程度.我们不得不自己写ToolBar.

思路

每个页面的xml写一份AppBar肯定是不理想的做法.所以大部分小伙伴可能采用include的方式.确实这样开发效率和代码阅读能力都有了一定的提高.但是.能不能在此基础上再做优化呢?答案是肯定的啦.

我们可以在include布局的基础上,把我们自定义ToolBar的控件获取、资源设置、事件处理全部封装起来,交给父类来处理.然后继承该父类(父类是我们自己写的,SDK没有哟,各位注意).

这样我们的xml通过include进行了处理,activity中的大量逻辑也被父类进行了处理.我们只需要进行初始化方法的调用以及事件处理即可.
先上效果图吧(看起来没什么,但是代码量极小哟)

效果

手把手教你打造通用AppBar(附Demo)_第1张图片
image


解释

我这边主要做了两种类型的封装.

一:标准样式的(左边返回icon,中间标题,右边的可点击文本)

二:自定义样式的(左边两个可点击icon,中间标题,右边两个可点击icon)

不仅仅如此,背景、文本颜色都可自行修改,也可指定某些文本、icon是否显示(内部处理了statusBar颜色高度等问题).

实现

一:我们需要添加布局文件,通过include布局实现

  //标准样式:
  
  //自定义样式
  

二:接着继承AppBarActivity(标准样式)或者CustomAppBarActivity(自定义样式),里面封装了所有逻辑.但是它们是抽象的类,我们需要实现相关方法.

//继承AppBarActivity要实现的方法
//设置标题
@Override
protected String setAppBarTitle() {
    return "标准3";
}
//设置右边文本
@Override
protected String setAppBarRightTitle() {
    return "点我";
}
//设置返回键点击
@Override
protected void onAppBarBackClick() {
    finish();
}
//设置右边文本点击
@Override
protected void onAppBarRightClick() {
    Toast.makeText(this, "点击", Toast.LENGTH_SHORT).show();
}
------------------------------分割------------------------------
//继承CustomAppBarActivity要实现的方法
//设置标题
 @Override
protected String setAppBarTitle() {
    return "自定义2";
}
//设置icon资源(不显示的返回-1即可)
@Override
protected int[] setAppBarDrawableRes() {
    return new int[]{R.drawable.left_arrow, R.drawable.edit, R.drawable.no_star, R.drawable.share};
}
//四个icon的点击事件
@Override
protected void onAppBarClick(int position) {
    switch (position) {
        case 0:
            finish();
            break;
        case 1:
            Toast.makeText(this, "编辑", Toast.LENGTH_SHORT).show();
            break;
        case 2:
            if (mIsStared) {
                Toast.makeText(this, "取消收藏", Toast.LENGTH_SHORT).show();
                setThreeIVDrawable(R.drawable.no_star);
            } else {
                Toast.makeText(this, "收藏", Toast.LENGTH_SHORT).show();
                setThreeIVDrawable(R.drawable.star);
            }
            mIsStared = !mIsStared;
            break;
        case 3:
            Toast.makeText(this, "分享", Toast.LENGTH_SHORT).show();
            break;
    }
}

三:最后调用initAppBar() 进行最后的初始化.指定了颜色、是否显示等状态(需在setContentView()方法之后.
注:initAppBar是个多参数重载方法,实现具体功能.

四:父类实现代码(Demo下载即可,非重点了解对象)

///标准样式父类实现
public abstract class AppBarActivity extends AppCompatActivity {

protected void initAppBar() {
    initAppBar(true, false, -1, -1);
}

protected void initAppBar(boolean isBack) {
    initAppBar(isBack, false, -1, -1);
}

protected void initAppBar(boolean isBack, boolean isRightText) {
    initAppBar(isBack, isRightText, -1, -1);
}

protected void initAppBar(boolean isBack, boolean isRightText, @ColorRes int bgColor, @ColorRes int textColor) {
    RelativeLayout layout = (RelativeLayout) findViewById(R.id.common_appbar_rl);
    LinearLayout linearLayout = (LinearLayout) findViewById(R.id.common_appbar_ll);
    if (layout == null) {
        return;
    }
    invadeStatusBar();
    if (bgColor != -1) {
        linearLayout.setBackgroundResource(bgColor);
    }
    LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) layout.getLayoutParams();
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        layoutParams.setMargins(0, getStatusBarHeight(), 0, 0);
    } else {
        layoutParams.setMargins(0, 0, 0, 0);
    }
    layout.setLayoutParams(layoutParams);
    ImageView iconIV = (ImageView) findViewById(R.id.common_appbar_iv);
    if (!isBack) {
        iconIV.setVisibility(View.GONE);
    } else {
        iconIV.setVisibility(View.VISIBLE);
        iconIV.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                onAppBarBackClick();

            }
        });
    }
    TextView centerTV = (TextView) findViewById(R.id.common_appbar_center_tv);
    if (textColor != -1) {
        centerTV.setTextColor(getResources().getColor(textColor));
    }
    centerTV.setText(TypeUtil.isBlank(setAppBarTitle()) ? "" : setAppBarTitle());
    TextView rightTV = (TextView) findViewById(R.id.common_appbar_right_tv);
    if (!isRightText) {
        rightTV.setVisibility(View.GONE);
    } else {
        rightTV.setVisibility(View.VISIBLE);
        if (textColor != -1) {
            centerTV.setTextColor(getResources().getColor(textColor));
        }
        rightTV.setText(TypeUtil.isBlank(setAppBarRightTitle()) ? "" : setAppBarRightTitle());
        rightTV.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                onAppBarRightClick();
            }
        });
    }
}


protected abstract String setAppBarTitle();

protected abstract String setAppBarRightTitle();

protected abstract void onAppBarBackClick();

protected abstract void onAppBarRightClick();

protected void invadeStatusBar() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        Window window = getWindow();
        window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
        window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
        window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
        window.setStatusBarColor(Color.TRANSPARENT);
    }
}

protected int getStatusBarHeight() {
    Resources resources = getResources();
    int resourceId = resources.getIdentifier("status_bar_height", "dimen", "android");
    int height = resources.getDimensionPixelSize(resourceId);
    Log.i("theHeight", height + "");
    if (height <= 10) {
        height = DensityUtil.dp2px(this, 16);
    }
    return height;
}
}

 ------------------------------分割----------------------------------------------
 
 //自定义样式父类实现
public abstract class CustomAppBarActivity extends AppCompatActivity {
private int[] mResArray;
private ImageView mThreeIV;

protected void initAppBar(boolean isOne, boolean isTwo, boolean isThree, boolean isFour) {
    initAppBar(isOne, isTwo, isThree, isFour, -1, -1);
}

protected void initAppBar(boolean isOne, boolean isTwo, boolean isThree, boolean isFour, @ColorRes int bgColor, @ColorRes int textColor) {
    RelativeLayout layout = (RelativeLayout) findViewById(R.id.custom_appbar_rl);
    LinearLayout linearLayout = (LinearLayout) findViewById(R.id.custom_appbar_ll);
    if (layout == null) {
        return;
    }
    invadeStatusBar();
    if (bgColor != -1) {
        linearLayout.setBackgroundResource(bgColor);
    }
    LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) layout.getLayoutParams();
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        layoutParams.setMargins(0, getStatusBarHeight(), 0, 0);
    } else {
        layoutParams.setMargins(0, 0, 0, 0);
    }
    layout.setLayoutParams(layoutParams);
    mResArray = setAppBarDrawableRes();
    TextView titleTV = (TextView) findViewById(R.id.custom_appbar_center_tv);
    if (textColor != -1) {
        titleTV.setTextColor(getResources().getColor(textColor));
    }
    titleTV.setText(setAppBarTitle() == null ? "默认标题" : setAppBarTitle());
    ImageView oneIV = (ImageView) findViewById(R.id.custom_appbar_one_iv);
    ImageView twoIV = (ImageView) findViewById(R.id.custom_appbar_two_iv);
    mThreeIV = (ImageView) findViewById(R.id.custom_appbar_three_iv);
    ImageView fourIV = (ImageView) findViewById(R.id.custom_appbar_four_iv);
    if (!isOne || mResArray.length < 1) {
        oneIV.setVisibility(View.GONE);
    } else {
        oneIV.setImageDrawable(getResources().getDrawable(mResArray[0]));
        oneIV.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                onAppBarClick(0);

            }
        });
    }
    if (!isTwo || mResArray.length < 2) {
        twoIV.setVisibility(View.GONE);
    } else {
        twoIV.setImageDrawable(getResources().getDrawable(mResArray[1]));
        twoIV.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                onAppBarClick(1);
            }
        });
    }
    if (!isThree || mResArray.length < 3) {
        mThreeIV.setVisibility(View.GONE);
    } else {
        mThreeIV.setImageDrawable(getResources().getDrawable(mResArray[2]));
        mThreeIV.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                onAppBarClick(2);
            }
        });
    }
    if (!isFour || mResArray.length < 4) {
        fourIV.setVisibility(View.GONE);
    } else {
        fourIV.setImageDrawable(getResources().getDrawable(mResArray[3]));
        fourIV.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                onAppBarClick(3);
            }
        });
    }
}

protected abstract String setAppBarTitle();

protected abstract int[] setAppBarDrawableRes();

protected abstract void onAppBarClick(int position);


protected void setThreeIVDrawable(@DrawableRes int drawable) {
    if (mThreeIV == null) {
        return;
    }
    mThreeIV.setImageDrawable(getResources().getDrawable(drawable));
}

protected void invadeStatusBar() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        Window window = getWindow();
        window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
        window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
        window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
        window.setStatusBarColor(Color.TRANSPARENT);
    }
}

private int getStatusBarHeight() {
    Resources resources = getResources();
    int resourceId = resources.getIdentifier("status_bar_height", "dimen", "android");
    int height = resources.getDimensionPixelSize(resourceId);
    Log.i("theHeight", height + "");
    if (height <= 10) {
        height = DensityUtil.dp2px(this, 16);
    }
    return height;
 }
 }

通过以上设置我们处理了布局加载、控件初始化、事件处理.三大块处理完毕了.相信逻辑上大家都明白了.具体实现细节是在AppBarActivity以及CustomAppBarActivity父类里面进行了封装处理.大家可以根据自己具体业务选择继承父类.或者再此基础上面进行修改调整.使其符合自己项目业务需要.

扩展

大家有没有想过,这样重复的在每个xml布局include头布局也是挺烦人的。能不能优化呢?答案是肯定的,但是会有微小问题。

代码实现

下面注释的三行便是动态加载头布局的实现了,首先获取我们activity的最外层布局,然后将具体的头文件布局添加到最外层布局的第0个index位置。

    //在父类完成,子类无需重复操作
    //ViewGroup outView = (ViewGroup) ((ViewGroup)findViewById(android.R.id.content)).getChildAt(0);
    // View inflate = LayoutInflater.from(this).inflate(R.layout.custom_appbar, null);
    //outView.addView(inflate,0);
    RelativeLayout layout = (RelativeLayout) findViewById(R.id.custom_appbar_rl);
    LinearLayout linearLayout = (LinearLayout) findViewById(R.id.custom_appbar_ll);
动态加载布局问题

该动态加载方法的唯一弊端,我们在写布局的时候,design预览页面会出现导航栏空白,如果是xml的include头布局方式则不会有这个问题。毕竟是代码动态加载布局的,此时未载入。当然这个也没有太大影响,使用方式因人而异吧。我会在仓库加入相关注释,大家可切换尝试。

总结

主要是思路的处理,以及细节封装.我这边有已经做好的Demo,可供大家交流以及直接使用.

地址: https://github.com/HoldMyOwn/AppBar

你可能感兴趣的:(手把手教你打造通用AppBar(附Demo))