美文网首页
自定义底部导航控件

自定义底部导航控件

作者: StoneWay3 | 来源:发表于2019-05-15 05:00 被阅读0次

忘了跟谁学习的了,写一个自定义的底部导航控件:直接上代码

import android.annotation.SuppressLint;

import android.content.Context;

import android.graphics.Bitmap;

import android.graphics.Canvas;

import android.graphics.Color;

import android.graphics.Paint;

import android.graphics.Rect;

import android.graphics.drawable.BitmapDrawable;

import android.support.annotation.Nullable;

import android.support.v4.app.Fragment;

import android.support.v4.app.FragmentTransaction;

import android.support.v7.app.AppCompatActivity;

import android.util.AttributeSet;

import android.view.MotionEvent;

import android.view.View;

import java.util.ArrayList;

import java.util.List;

public class BottomBarextends View {

private Contextcontext;

    public BottomBar(Context context, @Nullable AttributeSet attrs) {

super(context, attrs);

        this.context = context;

    }

//////////////////////////////////////////////////

//提供的api 并且根据api做一定的物理基础准备

//////////////////////////////////////////////////

    private int containerId;

    private ListfragmentClassList =new ArrayList<>();

    private ListtitleList =new ArrayList<>();

    private ListiconResBeforeList =new ArrayList<>();

    private ListiconResAfterList =new ArrayList<>();

    private ListfragmentList =new ArrayList<>();

    private int itemCount;

    private Paintpaint =new Paint();

    private ListiconBitmapBeforeList =new ArrayList<>();

    private ListiconBitmapAfterList =new ArrayList<>();

    private ListiconRectList =new ArrayList<>();

    private int currentCheckedIndex;

    private int firstCheckedIndex;

    private int titleColorBefore = Color.parseColor("#999999");

    private int titleColorAfter = Color.parseColor("#ff5d5e");

    private int titleSizeInDp =10;

    private int iconWidth =20;

    private int iconHeight =20;

    private int titleIconMargin =5;

    public BottomBarsetContainer(int containerId) {

this.containerId = containerId;

return this;

    }

public BottomBarsetTitleBeforeAndAfterColor(String beforeResCode, String AfterResCode) {//支持"#333333"这种形式

        titleColorBefore = Color.parseColor(beforeResCode);

        titleColorAfter = Color.parseColor(AfterResCode);

return this;

    }

public BottomBarsetTitleSize(int titleSizeInDp) {

this.titleSizeInDp = titleSizeInDp;

return this;

    }

public BottomBarsetIconWidth(int iconWidth) {

this.iconWidth = iconWidth;

return this;

    }

public BottomBarsetTitleIconMargin(int titleIconMargin) {

this.titleIconMargin = titleIconMargin;

return this;

    }

public BottomBarsetIconHeight(int iconHeight) {

this.iconHeight = iconHeight;

return this;

    }

public BottomBaraddItem(Class fragmentClass, String title, int iconResBefore, int iconResAfter) {

fragmentClassList.add(fragmentClass);

        titleList.add(title);

        iconResBeforeList.add(iconResBefore);

        iconResAfterList.add(iconResAfter);

return this;

    }

public BottomBarsetFirstChecked(int firstCheckedIndex) {//从0开始

        this.firstCheckedIndex = firstCheckedIndex;

return this;

    }

public void build() {

itemCount =fragmentClassList.size();

        //预创建bitmap的Rect并缓存

//预创建icon的Rect并缓存

        for (int i =0; i

Bitmap beforeBitmap = getBitmap(iconResBeforeList.get(i));

            iconBitmapBeforeList.add(beforeBitmap);

            Bitmap afterBitmap = getBitmap(iconResAfterList.get(i));

            iconBitmapAfterList.add(afterBitmap);

            Rect rect =new Rect();

            iconRectList.add(rect);

            Class clx =fragmentClassList.get(i);

            try {

Fragment fragment = (Fragment) clx.newInstance();

                fragmentList.add(fragment);

            }catch (InstantiationException | IllegalAccessException e) {

e.printStackTrace();

            }

}

currentCheckedIndex =firstCheckedIndex;

        switchFragment(currentCheckedIndex);

        invalidate();

    }

private BitmapgetBitmap(int resId) {

BitmapDrawable bitmapDrawable = (BitmapDrawable)context.getResources().getDrawable(resId);

        return bitmapDrawable.getBitmap();

    }

//////////////////////////////////////////////////

//初始化数据基础

//////////////////////////////////////////////////

    @Override

    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {

super.onLayout(changed, left, top, right, bottom);

        initParam();

    }

private int titleBaseLine;

    private ListtitleXList =new ArrayList<>();

    private int parentItemWidth;

    private void initParam() {

if (itemCount !=0) {

//单个item宽高

            parentItemWidth = getWidth() /itemCount;

            int parentItemHeight = getHeight();

            //图标边长

            int iconWidth = dp2px(this.iconWidth);//先指定20dp

            int iconHeight = dp2px(this.iconHeight);

            //图标文字margin

            int textIconMargin = dp2px(((float)titleIconMargin)/2);//先指定5dp,这里除以一半才是正常的margin,不知道为啥,可能是图片的原因

//标题高度

            int titleSize = dp2px(titleSizeInDp);//这里先指定10dp

            paint.setTextSize(titleSize);

            Rect rect =new Rect();

            paint.getTextBounds(titleList.get(0), 0, titleList.get(0).length(), rect);

            int titleHeight = rect.height();

            //从而计算得出图标的起始top坐标、文本的baseLine

            int iconTop = (parentItemHeight - iconHeight - textIconMargin - titleHeight)/2;

            titleBaseLine = parentItemHeight - iconTop;

            //对icon的rect的参数进行赋值

            int firstRectX = (parentItemWidth - iconWidth) /2;//第一个icon的左

            for (int i =0; i

int rectX = i *parentItemWidth + firstRectX;

                Rect temp =iconRectList.get(i);

                temp.left = rectX;

                temp.top = iconTop;

                temp.right = rectX + iconWidth;

                temp.bottom = iconTop + iconHeight;

            }

//标题(单位是个问题)

            for (int i =0; i

String title =titleList.get(i);

                paint.getTextBounds(title, 0, title.length(), rect);

                titleXList.add((parentItemWidth - rect.width()) /2 +parentItemWidth * i);

            }

}

}

private int dp2px(float dpValue) {

float scale =context.getResources().getDisplayMetrics().density;

        return (int) (dpValue * scale +0.5f);

    }

//////////////////////////////////////////////////

//根据得到的参数绘制

//////////////////////////////////////////////////

    @Override

    protected void onDraw(Canvas canvas) {

super.onDraw(canvas);//这里让view自身替我们画背景 如果指定的话

        if (itemCount !=0) {

//画背景

            paint.setAntiAlias(false);

            for (int i =0; i

Bitmap bitmap =null;

                if (i ==currentCheckedIndex) {

bitmap =iconBitmapAfterList.get(i);

                }else {

bitmap =iconBitmapBeforeList.get(i);

                }

Rect rect =iconRectList.get(i);

                canvas.drawBitmap(bitmap, null, rect, paint);//null代表bitmap全部画出

            }

//画文字

            paint.setAntiAlias(true);

            for (int i =0; i

String title =titleList.get(i);

                if (i ==currentCheckedIndex) {

paint.setColor(titleColorAfter);

                }else {

paint.setColor(titleColorBefore);

                }

int x =titleXList.get(i);

                canvas.drawText(title, x, titleBaseLine, paint);

            }

}

}

//////////////////////////////////////////////////

//点击事件:我观察了微博和掌盟,发现down和up都在该区域内才响应

//////////////////////////////////////////////////

    int target = -1;

    @SuppressLint("ClickableViewAccessibility")

@Override

    public boolean onTouchEvent(MotionEvent event) {

switch (event.getAction()) {

case MotionEvent.ACTION_DOWN :

target = withinWhichArea((int)event.getX());

break;

            case MotionEvent.ACTION_UP :

if (event.getY() <0) {

break;

                }

if (target == withinWhichArea((int)event.getX())) {

//这里触发点击事件

                    switchFragment(target);

                    currentCheckedIndex =target;

                    invalidate();

                }

target = -1;

break;

        }

return true;

        //这里return super为什么up执行不到?是因为return super的值,全部取决于你是否

//clickable,当你down事件来临,不可点击,所以return false,也就是说,而且你没

//有设置onTouchListener,并且控件是ENABLE的,所以dispatchTouchEvent的返回值

//也是false,所以在view group的dispatchTransformedTouchEvent也是返回false,

//这样一来,view group中的first touch target就是空的,所以intercept标记位

//果断为false,然后就再也进不到循环取孩子的步骤了,直接调用dispatch-

// TransformedTouchEvent并传孩子为null,所以直接调用view group自身的dispatch-

// TouchEvent了

    }

private int withinWhichArea(int x) {return x/parentItemWidth; }//从0开始

//////////////////////////////////////////////////

//碎片处理代码

//////////////////////////////////////////////////

    private FragmentcurrentFragment;

    //注意 这里是只支持AppCompatActivity 需要支持其他老版的 自行修改

    protected void switchFragment(int whichFragment) {

Fragment fragment =fragmentList.get(whichFragment);

        int frameLayoutId =containerId;

        if (fragment !=null) {

FragmentTransaction transaction = ((AppCompatActivity)context).getSupportFragmentManager().beginTransaction();

            if (fragment.isAdded()) {

if (currentFragment !=null) {

transaction.hide(currentFragment).show(fragment);

                }else {

transaction.show(fragment);

                }

}else {

if (currentFragment !=null) {

transaction.hide(currentFragment).add(frameLayoutId, fragment);

                }else {

transaction.add(frameLayoutId, fragment);

                }

}

currentFragment = fragment;

            transaction.commit();

        }

}

}

使用方法:

主activity的布局

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:orientation="vertical">

<FrameLayout

    android:id="@+id/fl_container"

    android:layout_width="match_parent"

    android:layout_height="0dp"

    android:layout_weight="1">

</FrameLayout>

<com.example.bottombar.BottomBar

    android:background="#FFFFFF"

    android:id="@+id/bottom_bar"

    android:layout_width="match_parent"

    android:layout_height="46dp"

    android:layout_gravity="bottom" />

</LinearLayout>

用法:

public class MainActivityextends AppCompatActivity {

@Override

    protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

        BottomBar bottomBar = findViewById(R.id.bottom_bar);

        bottomBar.setContainer(R.id.fl_container)

.setTitleBeforeAndAfterColor("#999999", "#ff5d5e")

.addItem(Fragment1.class,

                        "首页",

                        R.drawable.item1_before,

                        R.drawable.item1_after)

.addItem(Fragment2.class,

                        "订单",

                        R.drawable.item2_before,

                        R.drawable.item2_after)

.addItem(Fragment3.class,

                        "我的",

                        R.drawable.item3_before,

                        R.drawable.item3_after)

.build();

    }

}

相关文章

网友评论

      本文标题:自定义底部导航控件

      本文链接:https://www.haomeiwen.com/subject/omfuaqtx.html