仿文件文件管理器路径导航

项目的需要,做一个文件浏览器,结合大多数的文件管理器的特点,参考了华为手机的文件管理器的路径导航条,自己写了一个类似的路径导航布局器,不过这个布局器里面的水平布局器不能再创建的时候通过代码修改,比较不友好,如果有人解决还请告知一下,废话不说了,直接上代码。完整代码如下:
PathView.java 路径视图类

package com.ble.view;  //在自己的项目中请修改该包路径

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Shader;
import android.graphics.Typeface;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Build;
import android.os.Handler;
import android.text.TextPaint;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.HorizontalScrollView;
import android.widget.LinearLayout;

import com.ble.R;   //该引用需要修改成自己项目资源的引用

import java.util.Timer;
import java.util.TimerTask;

import androidx.annotation.ColorInt;
import androidx.annotation.Nullable;
import androidx.appcompat.content.res.AppCompatResources;

public class PathView extends View {
    /**最小宽度*/
    private final int MIN_WIDTH= dp2px(30);
    /**默认长按时间*/
    private final static int DEFAULT_LONG_PRESS_TIME=500;
    /**计算偏移*/
    private final int CALCULATE_OFFSET=4;
    /**长按超过0.5秒,触发长按事件*/
    private   int longPressTime= DEFAULT_LONG_PRESS_TIME;
    TextPaint textPaint;
    Paint arrowPaint;
    /**需要显示的文本*/
    private String text;
    /**路径*/
    private String path;
    /**文本颜色*/
    private @ColorInt int textColor= Color.BLACK;
    /**字体大小*/
    private int textSize=sp2px(14);
    /**字体样式**/
    private int textStyle=0;
    /**字体*/
    private Typeface typeface;
    /**分隔条颜色*/
    private @ColorInt int splitBarColor=0xff808080;
    /**显示内凹箭头*/
    private boolean concave=true;
    /**显示外凸箭头*/
    private boolean convex=true;
    /**分隔条*/
    private boolean  splitBar=true;
    /**四边保留位置*/
    private Rect padding=new Rect();
    /**绘制区与边的偏移量*/
    private int paddingOffset=4;
    /**内凹箭头宽度*/
    private float concaveArrowWidth;
    /**外凸箭头宽度*/
    private float convexArrowWidth;
   /**最大宽度*/
   private int maxWidth= dp2px(150);
   /**最小宽度*/
   private int minWidth=MIN_WIDTH;
   /**背景*/
    private Drawable background;
    /**按下背景*/
    private Drawable pressedBackground;
    /**按下*/
    private boolean isPressed=false;
    /**使用长按事件*/
    private boolean usingLongClick;
    /**不穿透*/
    private boolean noPenetration=false;
    /**上一次点击的的坐标*/
    private float last_x,last_y;
    /**计时器,计时点击时长*/
    private Timer timer;
    /**长按时间处理*/
    private TimerTask timerTaskLongDown;
    /**d定义中心按钮单击事件*/
    private OnClickListener onClickListener;
    /**定义中心按钮长按事件*/
    private OnLongClickListener onLongClickListener;
    private PathNavigationLayout rootLayout=null;
    /**滚动目录标题按钮水平滚动条线程*/
    private final Handler mHandler=new Handler();

    public PathView(Context context) {
        this(context,null);
    }
    public PathView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs,0);
    }
    @SuppressLint("LongLogTag")
    public PathView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        TypedArray typedArray=context.obtainStyledAttributes(attrs, R.styleable.PathView);
        text=typedArray.getString(R.styleable.PathView_android_text);
        textColor=typedArray.getColor(R.styleable.PathView_android_textColor,textColor);
        textSize=typedArray.getDimensionPixelOffset(R.styleable.PathView_android_textSize,textSize);
        textStyle=typedArray.getInteger(R.styleable.PathView_android_textStyle,textStyle);
        int typefaceIndex=typedArray.getInteger(R.styleable.PathView_android_typeface,Typeface.NORMAL);
        if(typefaceIndex==1){
            this.typeface= Typeface.create(Typeface.SANS_SERIF,textStyle );
        } else if ( typefaceIndex == 2 ) {
            this.typeface=  Typeface.create(Typeface.SERIF,textStyle );
        } else if ( typefaceIndex == 3 ) {
            this.typeface=  Typeface.create(Typeface.MONOSPACE, textStyle);
        }else{
            this.typeface=  Typeface.defaultFromStyle(textStyle);
        }
        splitBarColor=typedArray.getColor(R.styleable.PathView_splitBarColor,splitBarColor);
        concave=typedArray.getBoolean(R.styleable.PathView_concave,concave);
        convex=typedArray.getBoolean(R.styleable.PathView_convex,convex);
        maxWidth=typedArray.getDimensionPixelOffset(R.styleable.PathView_maxWidth,maxWidth);

        int resId=typedArray.getResourceId(R.styleable.PathView_pressedBackground,0);
        if(resId==0){
            pressedBackground=new ColorDrawable(0xffa0a0a0);
        }else{
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                pressedBackground=getContext().getDrawable(resId);
            }else {
                pressedBackground = AppCompatResources.getDrawable(getContext(), resId);
            }
        }
        boolean isClickable=typedArray.getBoolean(R.styleable.PathView_android_clickable,true);
        this.setClickable(isClickable);
        boolean LongClickable=typedArray.getBoolean(R.styleable.PathView_android_longClickable,true);
        this.setLongClickable(LongClickable);
        int val=typedArray.getInt(R.styleable.PathView_android_focusable,0);
        if(val==0) {
            this.setFocusable(typedArray.getBoolean(R.styleable.PathView_android_focusable, true));
        }else{
            this.setFocusable(true);
        }
        textPaint=new TextPaint(TextPaint.ANTI_ALIAS_FLAG);
        textPaint.setTextSize(textSize);
        textPaint.setColor(textColor);
        textPaint.setTypeface(typeface);
        arrowPaint=new Paint(Paint.ANTI_ALIAS_FLAG);
        arrowPaint.setStyle(Paint.Style.STROKE);
        arrowPaint.setColor(splitBarColor);
        typedArray.recycle();
    }
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        ViewGroup view= (ViewGroup) this.getParent();
        boolean isSetLayoutParams=false;//标志用于判断是否重新设置LayoutParams
        if(view instanceof LinearLayout){
            boolean  isHorizontal=((LinearLayout) view).getOrientation()==LinearLayout.HORIZONTAL?true:false;
            ViewGroup  roonView= (ViewGroup) view.getParent();
            if(roonView!=null && roonView instanceof PathNavigationLayout) {
                rootLayout= (PathNavigationLayout) roonView;
                //得到view在父容器中的位置下标
                int index = view.indexOfChild(this);
                if(isHorizontal) {
                    if (index == 0 && index == view.getChildCount() - 1) {
                        this.concave = false;
                        this.convex = false;
                        splitBar = false;
                    } else {
                        if (index == 0) {
                            this.concave = false;
                            splitBar = true;
                        } else if (index == view.getChildCount() - 1) {
                            this.convex = false;
                            splitBar = false;
                            isSetLayoutParams = true;
                            mHandler.post(mScrollTo);
                        } else {
                            this.concave = true;
                            this.convex = true;
                            splitBar = true;
                            isSetLayoutParams = true;
                        }
                    }
                }else{
                    this.concave = false;
                    this.convex = true;
                    splitBar = false;
                    isSetLayoutParams = false;
                }
                if(text==null || text.length()==0){
                    text= getContext().getString(R.string.label_path)+(index+1);
                }
//                this.setFocusable(true);
//                this.setClickable(true);
//                this.setLongClickable(true);
            }
        }
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
            minWidth=this.getMinimumWidth();
        }
        if(minWidth<=0){
            minWidth=MIN_WIDTH;
        }
        padding.top=getPaddingTop()+paddingOffset;
        padding.bottom=getPaddingBottom()+paddingOffset;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
            padding.left=(getPaddingStart()==0?getPaddingLeft():getPaddingStart())+paddingOffset;
            padding.right=(getPaddingEnd()==0?getPaddingRight():getPaddingEnd())+paddingOffset;
        }else{
            padding.left=getPaddingLeft()+paddingOffset;
            padding.right=getPaddingRight()+paddingOffset;
        }
        float textWidth=(text!=null?textPaint.measureText(text):0);//文本宽度
        float textHeight=textPaint.descent() - textPaint.ascent();//文本高度

        concaveArrowWidth=concave?(textHeight+padding.top+padding.bottom)/2:0;
        convexArrowWidth=convex?(textHeight+padding.top+padding.bottom)/2:0;
        int tempWidth= (int) (textWidth+concaveArrowWidth+convexArrowWidth+padding.left+padding.right); //得到总宽度需求
        int width=tempWidth>maxWidth?maxWidth:(tempWidth<minWidth?minWidth:tempWidth+CALCULATE_OFFSET);
        int height= (int) textHeight+padding.top+padding.bottom;
        if(isSetLayoutParams){
            ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) this.getLayoutParams();//得到属性参数
            lp.leftMargin = -height/2;
            this.setLayoutParams(lp);
        }
        setMeasuredDimension(width, height);
    }
    /**
     * 执行滚动条自动滚动到最后
     */
    private Runnable mScrollTo=new Runnable() {
        @Override
        public void run() {
            rootLayout.fullScroll(HorizontalScrollView.FOCUS_RIGHT);//滚动到最右边
        }
    };
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if(this.isFocusable()) { //focusable
            float x = event.getX();
            float y = event.getY();
            if (event.getAction() == MotionEvent.ACTION_DOWN) { //触摸按下
                if (isInterior((int) x, (int) y)) {
                    if(this.isLongClickable()) {
                        timer = new Timer();//长按计时器
                        timerTaskLongDown = new TimerTask() {
                            @Override
                            public void run() {
                                usingLongClick = false;
                                if (onLongClickListener != null) {
                                    usingLongClick = onLongClickListener.onLongClick(PathView.this);  //触发长按事件
                                }
                                Log.e("onLongClickListener",PathView.this.text);
                            }
                        };
                        timer.schedule(timerTaskLongDown, longPressTime);
                        //记录上次点击的位置,用来进行移动的模糊处理
                        last_x = x;
                        last_y = y;
                    }
                    isPressed = true;
                    invalidate();
                    noPenetration=true; //标志为非触摸穿透
                }
            } else if (event.getAction() == MotionEvent.ACTION_MOVE) { //触摸移动
                if (isPressed) {
                    if (Math.abs(last_x - x) > 20 || Math.abs(last_y - y) > 20) {   //判断是否移动
                        isPressed = false;
                        //取消计时
                        if (timerTaskLongDown != null) timerTaskLongDown.cancel();
                        if (timer != null) timer.cancel();
                    } else {
                        return true;
                    }
                }
            } else if (event.getAction() == MotionEvent.ACTION_UP) {//触摸弹起
                if (isPressed && !usingLongClick && this.isClickable()) { //按钮被按下
                    if (onClickListener != null) {
                        onClickListener.onClick(this);
                    }
                    Log.e("onClickListener",this.text);
                }
                //取消计时
                if (timerTaskLongDown != null) timerTaskLongDown.cancel();
                if (timer != null) timer.cancel();
                isPressed = false;
                usingLongClick = false;
                invalidate();
            }
            if(noPenetration){
                noPenetration=false;
                return true;  //处理掉触摸事件
            }else{
                if (timerTaskLongDown != null) timerTaskLongDown.cancel();
                if (timer != null) timer.cancel();
                isPressed = false;
                usingLongClick = false;
                invalidate();
                return false; //不处理触摸事件,让触摸穿透到下层视图
            }
        }else{
            if (timerTaskLongDown != null) timerTaskLongDown.cancel();
            if (timer != null) timer.cancel();
            isPressed = false;
            usingLongClick = false;
            invalidate();
            return false;//不处理触摸事件,让触摸穿透到下层视图
        }
    }
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //int textLeft; //字符串绘制的左边坐标
        Path path=new Path();
        //获取区域路径
        if(concave && convex) {
            path.moveTo(0, 0);
            path.lineTo(getMeasuredWidth() - getMeasuredHeight() / 2, 0);
            path.lineTo(getMeasuredWidth(), getMeasuredHeight() / 2);
            path.lineTo(getMeasuredWidth() - getMeasuredHeight() / 2, getMeasuredHeight());
            path.lineTo(0, getMeasuredHeight());
            path.lineTo(getMeasuredHeight() / 2, getMeasuredHeight() / 2);
           // textLeft= (int) concaveArrowWidth;
        }else{
            if(concave) {
                path.moveTo(0, 0);
                path.lineTo(getMeasuredWidth(), 0);
                path.lineTo(getMeasuredWidth(), getMeasuredHeight());
                path.lineTo(0, getMeasuredHeight());
                path.lineTo(getMeasuredHeight() / 2, getMeasuredHeight() / 2);
              //  textLeft=(int) concaveArrowWidth;
            }else if(convex){
                path.moveTo(0, 0);
                path.lineTo(getMeasuredWidth() - getMeasuredHeight() / 2, 0);
                path.lineTo(getMeasuredWidth(), getMeasuredHeight() / 2);
                path.lineTo(getMeasuredWidth() - getMeasuredHeight() / 2, getMeasuredHeight());
                path.lineTo(0, getMeasuredHeight());
              //  textLeft=0;
            }else{
                path.addRect(0,0,getMeasuredWidth(),getMeasuredHeight(), Path.Direction.CW);
              //  textLeft=0;
            }
        }
        //得到视图背景
        if(background==null) {
            background = getBackground();
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
                setBackground(null);
            } else {
                setBackgroundResource(0);
            }
        }
        //填充背景
        Drawable tempBack=this.isPressed?pressedBackground:background;
        if(tempBack!=null) {
            Paint backPaint=new Paint(Paint.ANTI_ALIAS_FLAG| Paint.DITHER_FLAG);
            if (tempBack instanceof ColorDrawable) {
                backPaint.setStyle(Paint.Style.FILL);
                backPaint.setColor(((ColorDrawable) tempBack).getColor());
            } else {
               Bitmap tempBitmap = drawable2Bitmap(tempBack, getMeasuredWidth(), getMeasuredHeight());
                backPaint.setShader(new BitmapShader(tempBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP));
            }
            path.close();
            canvas.drawPath(path,backPaint);
        }
        //绘制标签文本
        drawText(canvas,concaveArrowWidth+padding.left, (getMeasuredWidth()-convexArrowWidth-padding.right-padding.left-concaveArrowWidth),text);
        if(splitBar) {
            path = new Path();
            //绘制分隔线,这里减1的目的时因为绘图时最右边与最底部会有一个像素时不在区域内部的
            if (convex) {
                path.moveTo(getMeasuredWidth() - getMeasuredHeight() / 2, 0);
                path.lineTo(getMeasuredWidth() - 1, getMeasuredHeight() / 2);
                path.lineTo(getMeasuredWidth() - getMeasuredHeight() / 2, getMeasuredHeight() - 1);
            } else {
                path.moveTo(getMeasuredWidth() - 1, 0);
                path.lineTo(getMeasuredWidth() - 1, getMeasuredHeight() - 1);
            }
            canvas.drawPath(path, arrowPaint);//绘制分隔条
        }
    }
    /**
     * 绘制文本,在画布水平居中
     * @param canvas 画布
     * @param left 文本绘制的开始坐标
     * @param width 文本绘制的宽度
     * @param text 熏陶绘制的文本
     */
    private void drawText(Canvas canvas,float left,float width,String text){
        if(text!=null && text.length()>0) {
            float textWidth = textPaint.measureText(text);
            String subText = null;
            if (textWidth > width) {
                float oneWordWidth = textPaint.measureText(".");//得到单个字的宽度
                float ellipsizedWidth = oneWordWidth * 3; //获取省略好宽度
                int wordCount = (int) (width / oneWordWidth); //得到给定绘制宽度所能绘制的字符数
                if (text.length() < wordCount)
                    wordCount = text.length();
                for (int i = wordCount; i > 0; i--) {
                    //判断从开始到j位文本宽度加上省略号宽度是否小于或等于绘制宽度
                    if ((textPaint.measureText(text, 0, i) + ellipsizedWidth) <= width) {
                        subText = text.substring(0, i) + "..."; //得到开始到i位置的子字符串
                        break;
                    }
                }
            } else {
                subText = text;
            }
            float textHeight=textPaint.descent() - textPaint.ascent();//文本高度
            canvas.drawText(subText, left,(getMeasuredHeight()-textHeight)/2+textHeight/1.25f,textPaint);
        }
    }
    /**
     * drawable转换成bitmap
     * @param drawable
     * @param width
     * @param height
     * @return
     */
    private Bitmap drawable2Bitmap(Drawable drawable, int width, int height) {
        if (drawable == null)
            return null;
        Bitmap bitmap = Bitmap.createBitmap(  width, height,
                drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565);
        Canvas canvas = new Canvas(bitmap);
        drawable.setBounds(0, 0, width, height);
        drawable.draw(canvas);
        return bitmap;
    }

    /**
     * 判断指定点是否在异型视图内部
     * @param x
     * @param y
     * @return
     */
    private boolean isInterior(int x,int y) {
        Point p1,p2,p3;
        Rect rect=new Rect();  //存放矩形信息
        Point pn=new Point(x,y);
        boolean interior=false;
        if(concave){ //对内凹区判断
            p1=new Point(0,0);
            p2=new Point(getMeasuredHeight()/2,0);
            p3=new Point(getMeasuredHeight()/2,getMeasuredHeight()/2);
            interior=(isTriangleInterior(p1,p2,p3,pn));
            if(!interior){
                p1=new Point(0,getMeasuredHeight());
                p2=new Point(getMeasuredHeight()/2,getMeasuredHeight()/2);
                p3=new Point(getMeasuredHeight()/2,getMeasuredHeight());
                interior=(isTriangleInterior(p1,p2,p3,pn));
            }
            if(!interior){ //如果点不在内凹区两个三角形内
                rect.left=getMeasuredHeight()/2;
                rect.top=0;
            }
        }else{
            rect.left=0;
            rect.top=0;
        }
        if(!interior){
            if(convex){ //对外凸区判断
                p1=new Point(getMeasuredWidth()-getMeasuredHeight()/2,0);
                p2=new Point(getMeasuredWidth(),getMeasuredHeight()/2);
                p3=new Point(getMeasuredWidth()-getMeasuredHeight()/2,getMeasuredHeight());
                interior=(isTriangleInterior(p1,p2,p3,pn));
                if(!interior){
                    rect.right=getMeasuredWidth()-getMeasuredHeight()/2;
                    rect.bottom=getMeasuredHeight();
                }
            }else{
                rect.right=getMeasuredWidth();
                rect.bottom=getMeasuredHeight();
            }
            if(!interior){
                if(x>=rect.left && x<=rect.right && y>=rect.top && y<=rect.bottom){ //判断是否在矩形内部
                    interior=true;
                }
            }
        }
        return interior;
    }
    /**
     * 判断指定点是否在三角形内部
     * @param p1 三角形第一个点
     * @param p2 三角形第二个点
     * @param p3 三角形第三个点
     * @param pn 需要判断的点
     * @return true 在内部,false 不在
     */
    private boolean isTriangleInterior(Point p1,Point p2,Point p3,Point pn){
        //得到三角形三条边的长度
        float sideDistance_1 = (float) Math.sqrt(Math.pow(p1.y - p2.y, 2) + Math.pow(p1.x - p2.x, 2));
        float sideDistance_2= (float) Math.sqrt(Math.pow(p2.y - p3.y, 2) + Math.pow(p2.x - p3.x, 2));
        float sideDistance_3= (float) Math.sqrt(Math.pow(p3.y - p1.y, 2) + Math.pow(p3.x - p1.x, 2));
        //得到三角形三个顶点到pn点的组长度
        float  pnDistance_1=(float) Math.sqrt(Math.pow(p1.y - pn.y, 2) + Math.pow(p1.x - pn.x, 2));
        float  pnDistance_2=(float) Math.sqrt(Math.pow(p2.y - pn.y, 2) + Math.pow(p2.x - pn.x, 2));
        float  pnDistance_3=(float) Math.sqrt(Math.pow(p3.y - pn.y, 2) + Math.pow(p3.x - pn.x, 2));
        //判断p1点至pn点是否大于p1点到p2点,p1点到p3点边长,如果大于的话pn点不在三角形内部
        if(pnDistance_1>sideDistance_1 && pnDistance_1>sideDistance_3) {
            return false;
        //判断p2点至pn点是否大于p2点到p1点,p2点到p3点边长,如果大于的话pn点不在三角形内部
        }else if(pnDistance_2>sideDistance_1 && pnDistance_2>sideDistance_2){
            return false;
       //判断p3点至pn点是否大于p3点到p2点,p3点到p1点边长,如果大于的话pn点不在三角形内部
        }else if(pnDistance_3>sideDistance_2 && pnDistance_3>sideDistance_3){
            return false;
        }else{
           return true;
        }
    }

   /**************************公共属性及方法************************/
   /**显示内凹箭头*/
    public void setConcave(boolean concave) {
        this.concave = concave;
        requestLayout();
        invalidate();
    }
    /**显示内凹箭头*/
    public boolean isConcave() {
        return concave;
    }
    /**显示外凸箭头*/
    public void setConvex(boolean convex) {
        this.convex = convex;
        requestLayout();
        invalidate();
    }
    /**显示外凸箭头*/
    public boolean isConvex() {
        return convex;
    }
   /**内凹箭头宽度*/
    public float getConcaveArrowWidth() {
        return concaveArrowWidth;
    }
    /**外凸箭头宽度*/
    public float getConvexArrowWidth() {
        return convexArrowWidth;
    }
   /**文本*/
    public void setText(String text) {
        this.text = text;
        requestLayout();
        invalidate();
    }
    /**文本*/
    public String getText() {
        return text;
    }
    public void setTextStyle(int textStyle) {
        this.textStyle = textStyle;
        setTypeface(typeface);
    }
    public int getTextStyle() {
        return textStyle;
    }
    /**字体*/
    public void setTypeface(Typeface fimaly) {
        this.typeface =Typeface.create(fimaly,textStyle) ;
        if(textPaint==null) {
            textPaint = new TextPaint(TextPaint.ANTI_ALIAS_FLAG);
            textPaint.setTextSize(sp2px(14));
            textPaint.setColor(textColor);
        }
        textPaint.setTypeface(this.typeface);
        requestLayout();
        invalidate();
    }
    private void setTypeface(String  fimalyName){
        this.typeface =Typeface.create(fimalyName,textStyle) ;
        if(textPaint==null) {
            textPaint = new TextPaint(TextPaint.ANTI_ALIAS_FLAG);
            textPaint.setTextSize(sp2px(14));
            textPaint.setColor(textColor);
        }
        textPaint.setTypeface(this.typeface);
        requestLayout();
        invalidate();
    }
    public Typeface getTypeface() {
        return typeface;
    }
    public void setTextSize(int textSize) {
        this.textSize = textSize;
        if(textPaint==null) {
            textPaint = new TextPaint(TextPaint.ANTI_ALIAS_FLAG);
            textPaint.setColor(textColor);
            textPaint.setTypeface(this.typeface);
        }
        textPaint.setTextSize(this.textSize);
        requestLayout();
        invalidate();
    }
    public int getTextSize() {
        return textSize;
    }
   /**文本颜色*/
    public void setTextColor(@ColorInt int textColor) {
        this.textColor = textColor;
        if(textPaint==null) {
            textPaint = new TextPaint(TextPaint.ANTI_ALIAS_FLAG);
            textPaint.setTypeface(this.typeface);
            textPaint.setTextSize(this.textSize);
            requestLayout();
        }
        textPaint.setColor(this.textColor);
        invalidate();
    }
    /**文本颜色*/
    public void setTextColor(String textColor) {
        try{
            setTextColor(Color.parseColor(textColor));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public int getTextColor() {
        return textColor;
    }
    /**分隔条颜色**/
    public void setSplitBarColor(@ColorInt int splitBarColor) {
        this.splitBarColor = splitBarColor;
        if(arrowPaint==null)
            arrowPaint=new Paint(Paint.ANTI_ALIAS_FLAG);
        arrowPaint.setColor(this.splitBarColor);
        invalidate();
    }
    /**分隔条颜色**/
    public void setSplitBarColor(String splitBarColor) {
        try{
            setSplitBarColor(Color.parseColor(splitBarColor));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    /**分隔条颜色**/
    public int getSplitBarColor() {
        return splitBarColor;
    }
    /**分隔条*/
    public void setSplitBar(boolean splitBar) {
        this.splitBar = splitBar;
        invalidate();
    }
    /**分隔条*/
    public boolean isSplitBar() {
        return splitBar;
    }
    /***
     * 设置完整路径
     * @param path
     */
    public void setPath(String path) {
        this.path = path;
    }
    public void setPath(Uri uri){
        this.path=uri.getPath();
    }
    /**获取完整路径*/
    public String getPath() {
        return path;
    }
    /**获取路径URI*/
    public Uri getPathUri(){
        return Uri.parse(path);
    }
    @Override
    public void setOnClickListener(OnClickListener onClickListener) {
        this.onClickListener = onClickListener;
    }
    @Override
    public void setOnLongClickListener(OnLongClickListener onLongClickListener) {
        setOnLongClickListener(onLongClickListener,DEFAULT_LONG_PRESS_TIME);
    }

    /**
     * 设置长按侦听
     * @param onLongClickListener 长按侦听接口
     * @param longPressTime 长按触发时长(毫秒)
     */
    public void setOnLongClickListener(OnLongClickListener onLongClickListener,long longPressTime) {
        longPressTime=longPressTime;
        this.onLongClickListener = onLongClickListener;
    }
        /**
     * dp转像素
     * @param dpVal 需要转换的dp值
     * @return 返回像素未单位的值
     */
    private int dp2px(float dpVal) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
                dpVal,  this.getContext().getResources().getDisplayMetrics());
    }
        /** sp转换px */
    private int sp2px( int spVal) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
                spVal, this.getContext().getResources().getDisplayMetrics());
    }
}

在项目资源目录下创建attr.xml自定义视图属性文件

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="PathView">
        <!--显示文本内容-->
        <attr name="android:text"/>
        <!--文本大小-->
        <attr name="android:textSize"/>
        <!--文本颜色-->
        <attr name="android:textColor"/>
        <!--字体样式-->
        <attr name="android:textStyle" />
        <!--字体类型-->
        <attr name="android:typeface" />
        <!--分隔条颜色-->
        <attr name="splitBarColor" format="color"/>
        <!--内凹-->
        <attr name="concave" format="boolean"/>
        <!--外凸-->
        <attr name="convex" format="boolean"/>
        <!--最大宽度-->
        <attr name="maxWidth" format="dimension"/>
        <!--按下时背景-->
        <attr name="pressedBackground" format="color|reference"/>
        <!--单击事件(继承系统属性)-->
        <attr name="android:clickable"/>
        <!--长按事件(继承系统属性)-->
        <attr name="android:longClickable"/>
        <!--准许拥有焦点(继承系统属性)-->
        <attr name="android:focusable"/>
    </declare-styleable>
</resources>

PathNavigationLayout.java 导航布局器,对HorizontalScrollView的扩展

package com.ble.view;

import android.content.Context;
import android.graphics.Canvas;
import android.net.Uri;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.widget.HorizontalScrollView;
import android.widget.LinearLayout;

import androidx.annotation.NonNull;

public class PathNavigationLayout extends HorizontalScrollView implements View.OnClickListener{
    private LinearLayout mContainer;
    /**路径选择侦听接口*/
    public interface OnPathSelectedListener{
        /**
         * 路径选择事件
         * @param pathView 路径视图
         * @param position 位置
         * @param fromUser 用户改变
         * @return
         */
        boolean OnSelected(PathView pathView,int position,boolean fromUser);
    }
    private OnPathSelectedListener selectedListener;
    public PathNavigationLayout(Context context) {
        this(context,null);
    }
    public PathNavigationLayout(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }
    public PathNavigationLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        // 禁用滚动条
        setHorizontalScrollBarEnabled(false);
        mContainer= (LinearLayout) getChildAt(0);
        if(mContainer!=null)
            mContainer.setOrientation(LinearLayout.HORIZONTAL);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //mContainer= (LinearLayout) getChildAt(0);
    }

    @Override
    public void draw(Canvas canvas) {
        super.draw(canvas);
       // canvas.drawText(Tag,0,50,new Paint());
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
        if(mContainer!=null){
            int childCount =mContainer.getChildCount();
            for(int i = 0 ; i<childCount;i++){
                View child=mContainer.getChildAt(i);
                if(child instanceof PathView) {
                    PathView childPathView= (PathView) child;
                    childPathView.setOnClickListener(this);
                }
            }
        }
    }
    @Override
    public void addView(View child) {
        //Tag="addView(View child)";
        if(this.getChildCount()==0) {
            if(child instanceof ViewGroup) {
                super.addView(child);
            }
        }else{
            addViewInternal(child);
        }
    }
    @Override
    public void addView(View child, int index) {
        //Tag="addView(View child, int index)";
        if(this.getChildCount()==0) {
            if(child instanceof ViewGroup) {
                super.addView(child,index);
            }
        }else{
            addViewInternal(child);
        }
    }
    @Override
    public void addView(View child, int index, ViewGroup.LayoutParams params) {
        //Tag="addView(View child, int index, ViewGroup.LayoutParams params)";
        if(this.getChildCount()==0) {
            if(child instanceof ViewGroup) {
//                if(child instanceof LinearLayout){
//                    ((LinearLayout)child).setOrientation(LinearLayout.HORIZONTAL);
//                    child.requestLayout();
//                    child.invalidate();
//                    Tag="addView(View child, int index, ViewGroup.LayoutParams params)刷新";
//                }
                super.addView(child, index, params);
            }
        }else{
            addViewInternal(child);
        }
    }
    @Override
    public void addView(View child, ViewGroup.LayoutParams params) {
        //Tag="addView(View child, ViewGroup.LayoutParams params)";
        if(this.getChildCount()==0) {
            if(child instanceof ViewGroup) {
                super.addView(child, params);
            }
        }else{
            addViewInternal(child);
        }
    }

    @Override
    public void addView(View child, int width, int height) {
        //Tag="addView(View child, int width, int height)";
        if(this.getChildCount()==0) {
            if(child instanceof ViewGroup) {
                super.addView(child, width, height);
            }
        }else{
            addViewInternal(child);
        }
    }
    @Override
    public void removeAllViews() {
        super.removeAllViews();
    }

    @Override
    public void removeView(View view) {
        if(this.getChildCount()>0) {
            if(view instanceof ViewGroup){
                super.removeView(view);
            }else{
                ViewGroup viewGroup= (ViewGroup) this.getChildAt(0);
                viewGroup.removeView(view);
            }
        }
    }
    /**
     * 添加路径视图
     * @param pathView
     */
    public void addPathView(@NonNull PathView pathView){
        addViewInternal(pathView);
    }
    /**
     * 删除所有PathView视图
     */
    public void  removeAllPathViews(){
        if(this.getChildCount()>0) {
            ViewGroup viewGroup= (ViewGroup) this.getChildAt(0);
            for(int i=0;i<viewGroup.getChildCount();i++){
                if(viewGroup.getChildAt(i)instanceof  PathView){
                    viewGroup.removeViewAt(i);
                }
            }
        }
    }
    /**删除PathView视图*/
    public void removePathView(PathView pathView){
        if(this.getChildCount()>0) {
            ViewGroup viewGroup= (ViewGroup) this.getChildAt(0);
            viewGroup.removeView(pathView);
        }
    }

    /**
     * 删除指定位置的PathView,如果存在删除,如不存在则什么也不做
     * @param index 索引
     */
    public void removePathViewAt(int index) {
        if(this.getChildCount()>0) {
            ViewGroup viewGroup= (ViewGroup) this.getChildAt(0);
            if(viewGroup.getChildAt(index)instanceof  PathView) {
                viewGroup.removeViewAt(index);
            }
        }
    }
    /**
     * 删除指定索引的视图。
     * @param index -1表示删除当前视图下的第一个子视图,>=0表示删除第一个子视图下指定位置的子视图
     */
    @Override
    public void removeViewAt(int index) {
        if(this.getChildCount()>0) {
            if(index==-1) {
                super.removeViewAt(0);
            }else{
                ViewGroup viewGroup= (ViewGroup) this.getChildAt(0);
                if(index>=0 && index<viewGroup.getChildCount()) {
                    viewGroup.removeViewAt(index);
                }
            }
        }
    }

    /**
     * 新建路径视图
     * @param path 路径名称
     * @return
     */
    public PathView newPath(String path){
        PathView pathView=new PathView(this.getContext());
        if(path!=null && path.length()>0) {
            int position = path.lastIndexOf("/") + 1;//获取最后一个路径分隔符+1的位置
            String folder = path.substring(position);
            pathView.setPath(path);
            pathView.setText(folder);
        }
        return pathView;
    }

    /**
     * 新建路径视图
     * @param path 路径URI
     * @return
     */
    public PathView newPath(Uri path){
        return   newPath(path.getPath());
    }
    /**
     * 新建路径视图
     * @param path 路径名称
     * @param folder 文件夹或文件名
     * @return
     */
    public PathView newPath(String path,String folder){
        PathView pathView=new PathView(this.getContext());
        pathView.setText(folder);
        pathView.setPath(path);
        return pathView;
    }
    /**
     * 设置路径选择侦听
     * @param selectedListener
     */
    public void setOnSelectedListener(OnPathSelectedListener selectedListener) {
        this.selectedListener = selectedListener;
    }

    /**
     * 选中指定位置视图
     * @param position
     */
    public void setSelectedView(int position){
        if(getChildCount()>0) {
            View view = getChildAt(0);
            if(view instanceof  ViewGroup) {
                ViewGroup viewGroup = (ViewGroup) view;
                if (position < viewGroup.getChildCount()) {
                    View childView = viewGroup.getChildAt(position);
                    childView.setSelected(true);
                    if (childView instanceof PathView) {
                        if (selectedListener != null) {
                            if (selectedListener.OnSelected((PathView) childView, position, false)) {
                                //循环删除当前路径后面的所有路径视图
                                for(int i=viewGroup.getChildCount()-1;i>position;i--) {
                                    viewGroup.removeViewAt(i);
                                }
                            }
                        }
                    }
                    for(int i=0;i<viewGroup.getChildCount();i++){
                        View tempView = viewGroup.getChildAt(i);
                         if(tempView!=childView) {
                             viewGroup.getChildAt(i).setSelected(false);
                         }
                    }
                }
            }
        }
    }
    /***********************内部函数************************/
    /**
     * 内部添加视图
     * @param child
     */
    private void addViewInternal( View child) {
        if (child instanceof PathView) {
            if(this.getChildCount()==0){
                this.addView(new LinearLayout(this.getContext()));
            }
            ViewGroup viewGroup= (ViewGroup) this.getChildAt(0);
            viewGroup.addView(child);
        } else {
            throw new IllegalArgumentException("Only PathView instances can be added to PathNavigationLayout");
        }
    }
    @Override
    public void onClick(View v) {
        if(getChildCount()>0) {
            View view = getChildAt(0);
            if(view instanceof  ViewGroup){
                ViewGroup viewGroup= (ViewGroup) view;
                v.setSelected(true);
                int index=viewGroup.indexOfChild(v);
                if(v instanceof PathView) {
                    if (selectedListener != null) {
                        if(selectedListener.OnSelected((PathView) v,index,true)){
                            //循环删除当前路径后面的所有路径视图
                            for(int i=viewGroup.getChildCount()-1;i>index;i--) {
                                viewGroup.removeViewAt(i);
                            }
                        }
                    }
                }
                for(int i=0;i<viewGroup.getChildCount();i++){
                    View tempView = viewGroup.getChildAt(i);
                    if(tempView!=v) {
                        viewGroup.getChildAt(i).setSelected(false);
                    }
                }
            }
        }
    }
}

效果图如下:
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值