仿淘宝物流弹框

demo地址:https://github.com/newsupercode/WuliuNodeView
先看效果图:
这里写图片描述这里写图片描述

两种实现效果:
第一种是背景是透明度的:
ViewPager+TabLayout +fragment是在一个dialog形式的Activity上的
Dialogactivity的布局:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/transparent"
    android:orientation="vertical">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="500dp"
        android:layout_marginTop="23dp"
        android:background="@drawable/transparent"
        android:gravity="center_vertical">

        <android.support.v4.view.ViewPager
            android:id="@+id/vp_address_dialog"
            android:layout_width="match_parent"
            android:layout_height="match_parent">


            <android.support.design.widget.TabLayout
                android:id="@+id/tb_address_dialog"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:background="@drawable/transparent"
                app:tabGravity="center"
                app:tabIndicatorHeight="0dp" />

        </android.support.v4.view.ViewPager>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="40dp"
            android:layout_alignParentRight="true"
            android:layout_marginRight="20dp"
            android:background="@drawable/transparent"
            android:gravity="center"
            android:text="查看全部"
            android:textColor="#fff"
            android:textSize="14sp" />
    </RelativeLayout>

    <LinearLayout
        android:id="@+id/ll_close"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:layout_gravity="center"
        android:layout_marginBottom="20dp"
        android:layout_marginTop="10dp"
        android:background="@color/transparent"
        android:gravity="center">

        <ImageView
            android:id="@+id/close"
            android:layout_width="51dp"
            android:layout_height="51dp"
            android:layout_gravity="center"
            android:background="@drawable/g_close" />
    </LinearLayout>
</LinearLayout>

//tou透明度DialogAddressActivity 
public class DialogAddressActivity extends FragmentActivity {

    @BindView(R.id.tb_address_dialog)
    TabLayout tbAddressDialog;
    @BindView(R.id.vp_address_dialog)
    ViewPager vpAddressDialog;
    ArrayList<Fragment> list;
    MyAdapter myAdapter;
    @BindView(R.id.ll_close)
    LinearLayout llClose;
    private int ONE_ALPHE = 10;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_dialog_address);
        ButterKnife.bind(this);
//        getWindow().setLayout(WindowManager.LayoutParams.FILL_PARENT, WindowManager.LayoutParams.FILL_PARENT);
        //页面,数据源
        list = new ArrayList<>();
        list.add(new FirstAddressFragment());
        list.add(new FirstAddressFragment());
        myAdapter = new MyAdapter(getSupportFragmentManager());
        vpAddressDialog.setAdapter(myAdapter);
        //绑定
        tbAddressDialog.setupWithViewPager(vpAddressDialog);

        for (int i = 0; i < myAdapter.getCount(); i++) {
            TabLayout.Tab tab = tbAddressDialog.getTabAt(i);//获得每一个tab
            tab.setCustomView(R.layout.tab_item_dialog_address);//给每一个tab设置view
            if (i == 0) {
                // 设置第一个tab的TextView是被选择的样式
                ImageView imageView = tab.getCustomView().findViewById(R.id.iv_indicator);
                imageView.setBackgroundDrawable(getResources().getDrawable(R.drawable.viewpager2));
                imageView.setSelected(true);

            } else {
                //其他的灰色
                ImageView imageView = tab.getCustomView().findViewById(R.id.iv_indicator);
                imageView.setBackgroundDrawable(getResources().getDrawable(R.drawable.viewpager3));
                imageView.setSelected(false);
            }


            tbAddressDialog.setOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
                @Override
                public void onTabSelected(TabLayout.Tab tab) {
                    ImageView imageView = tab.getCustomView().findViewById(R.id.iv_indicator);
                    imageView.setBackgroundDrawable(getResources().getDrawable(R.drawable.viewpager2));
                    imageView.setSelected(true);
                    vpAddressDialog.setCurrentItem(tab.getPosition());
                }

                @Override
                public void onTabUnselected(TabLayout.Tab tab) {
                    ImageView imageView = tab.getCustomView().findViewById(R.id.iv_indicator);
                    imageView.setBackgroundDrawable(getResources().getDrawable(R.drawable.viewpager3));
                    imageView.setSelected(false);

                }

                @Override
                public void onTabReselected(TabLayout.Tab tab) {

                }
            });
        }
        llClose.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                finish();
            }
        });
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        return super.dispatchTouchEvent(ev);
    }


    class MyAdapter extends FragmentPagerAdapter {
        public MyAdapter(FragmentManager fm) {
            super(fm);
        }

        @Override
        public Fragment getItem(int position) {
            return list.get(position);
        }

        @Override
        public int getCount() {
            return list.size();
        }
    }
}

  <activity
            android:name=".mime.service.activity.DialogAddressActivity"
            android:theme="@style/transcutestyle" />
 <style name="Dialog_Fullscreen" parent="Theme.AppCompat">
        <item name="android:windowFullscreen">true</item>
        <item name="android:windowNoTitle">true</item>
        <item name="android:windowFrame">@null</item>
        <item name="android:windowIsFloating">true</item>
        <item name="android:windowContentOverlay">@null</item>
    </style>
    <!--dialog的activity全屏-->
    <style name="transcutestyle" parent="Dialog_Fullscreen">
        <item name="android:windowFrame">@android:color/transparent</item><!--边框-->
        <item name="android:windowIsFloating">true</item><!--是否浮现在activity之上-->
        <item name="android:windowIsTranslucent">true</item><!--半透明-->
        <item name="android:windowNoTitle">true</item><!--无标题-->
        <item name="android:windowBackground">@android:color/transparent</item><!--背景透明-->
        <item name="android:backgroundDimAmount">0.3</item>
        <item name="android:windowAnimationStyle">@null</item>
    </style>

第二种是截屏高斯模糊的背景

 new BlurPopWin.Builder(this).setContent("该配合你演出的我,眼视而不见,在比一个最爱你的人即兴表演")
                        //Radius越大耗时越长,被图片处理图像越模糊
                        .setRadius(3).setTitle("已到达")
                        //设置居中还是底部显示
                        .setshowAtLocationType(0)
                        .setShowCloseButton(true)
                        .setOutSideClickable(false)
                        .SetLogisticsDataList(logisticsDataList)
                        .onphoneclock(new BlurPopWin.OnPhoneClickListener() {
                            @Override
                            public void onphoneclock(String phoneNumber) {
//                                dialogCreateCall(phoneNumber);
                                //实现拨打电话的操作
                                Intent intent = new Intent(Intent.ACTION_CALL, Uri.parse("tel:"
                                        + phoneNumber));
                                if (ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.CALL_PHONE)
                                        != PackageManager.PERMISSION_GRANTED) {
                                    return;
                                }
                                startActivity(intent);
                            }
                        })
//                       .onClick(new BlurPopWin.PopupCallback() {
//                            @Override
//                            public void onClick(@NonNull BlurPopWin blurPopWin) {
//                                Toast.makeText(mContext, "中间被点了", Toast.LENGTH_SHORT).show();
//                                //跳转详情页
//                                Intent intent = new Intent(mContext, AddressInfoActivity.class);
//                                mContext.startActivity(intent);
//
//
//                                blurPopWin.dismiss();
//                            }
//                        })
                        .show(view);

这是带高斯模糊背景的Popwindow ,其中关于高斯模糊的算法就不列出来了,demo中有

public class BlurPopWin {
    private RelativeLayout pop_root_layout;
    private CardView pop_layout;
    private TextView title;
    private TextView content;
    private ImageView close;
    private Builder builder;
    private PopupWindow popupWindow;
    private int radius;
    private float touchY;
    private Bitmap localBit;

    public static final String GRAVITY_BOTTOM = "BOTTOM";
    public static final String GRAVITY_CENTER = "CENTER";
    private LogisticsInformationView logistics_InformationView;

    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
    public BlurPopWin(Builder builder) {
        this.builder = builder;
        builder.blurPopWin = initBlurPopWin(builder);
    }

    @UiThread
    public void show(View view) {
        builder.blurPopWin.popupWindow.showAtLocation(view, Gravity.CENTER, 0, 0);
    }

    @UiThread
    public void dismiss() {
        if (builder != null && builder.blurPopWin != null)
            builder.blurPopWin.popupWindow.dismiss();
    }

    /*
    截取屏幕
    * */
    @Nullable
    private Bitmap getIerceptionScreen() {
        // View是你需要截图的View
        View view = builder.activity.getWindow().getDecorView();
        view.setDrawingCacheEnabled(true);
        view.buildDrawingCache();
        Bitmap b = view.getDrawingCache();

        // 获取状态栏高度
        Rect frame = new Rect();
        builder.activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(frame);
        int statusBarHeight = frame.top;

        // 获取屏幕长和高
        int width = builder.activity.getWindowManager().getDefaultDisplay().getWidth();
        int height = builder.activity.getWindowManager().getDefaultDisplay()
                .getHeight();
        // 去掉标题栏
        // Bitmap b = Bitmap.createBitmap(b1, 0, 25, 320, 455);
        Bitmap bitmap = Bitmap.createBitmap(b, 0, statusBarHeight, width, height
                - statusBarHeight);
        view.destroyDrawingCache();
        bitmap = FastBlur.fastBlur(bitmap, radius);
        if (bitmap != null) {
            return bitmap;
        } else {
            return null;
        }
    }

    /*
    初始化
    * */
    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
    @UiThread
    private BlurPopWin initBlurPopWin(final Builder builder) {
        if (builder != null) {

            View rootView = builder.activity.getLayoutInflater().inflate(R.layout.pop_layout, null, false);
            popupWindow = new PopupWindow(rootView, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, true);
            pop_layout = (CardView) rootView.findViewById(R.id.pop_layout);
            pop_root_layout = (RelativeLayout) rootView.findViewById(R.id.pop_root_layout);
            title = (TextView) rootView.findViewById(R.id.title);
            content = (TextView) rootView.findViewById(R.id.content);
            close = (ImageView) rootView.findViewById(R.id.close);
            logistics_InformationView = rootView.findViewById(R.id.logistics_InformationView);

            if (builder.logisticsDataList != null) {
                logistics_InformationView.setLogisticsDataList(builder.logisticsDataList);

            }

//标题
            if (builder.title != null) {
                title.setText(builder.title);
            }

            if (builder.content != null) {
                content.setText(builder.content);
            }
//圆角
            if (builder.radius != 0) {
                radius = builder.radius;
            } else {
                radius = 5;
            }
//字号
            if (builder.titleTextSize != 0) {
                title.setTextSize(builder.titleTextSize);
            }

            if (builder.contentTextSize != 0) {
                content.setTextSize(builder.contentTextSize);
            }
//关闭
            if (builder.isShowClose) {
                close.setVisibility(View.VISIBLE);
                close.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View view) {
                        popupWindow.dismiss();
                    }
                });
            } else {
                close.setClickable(false);
                close.setVisibility(View.INVISIBLE);
            }
//显示位置
            RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
            if (builder.showAtLocationType.equals(GRAVITY_CENTER)) {
                lp.addRule(RelativeLayout.CENTER_VERTICAL, RelativeLayout.TRUE);
            } else {
                lp.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM, RelativeLayout.TRUE);
            }
            pop_layout.setLayoutParams(lp);
//截屏
            if (localBit == null) {
                localBit = getIerceptionScreen();
            }

            pop_root_layout.setBackground(new BitmapDrawable(localBit));

            pop_root_layout.setOnTouchListener(new View.OnTouchListener() {
                @Override
                public boolean onTouch(View view, MotionEvent motionEvent) {
                    switch (motionEvent.getAction()) {
                        case MotionEvent.ACTION_DOWN:
                            touchY = motionEvent.getY();
                            break;
                        case MotionEvent.ACTION_MOVE:
                            break;
                        case MotionEvent.ACTION_UP:

                            if (builder.isBackgroundClose) {
                                if (builder.showAtLocationType.equals(GRAVITY_CENTER)) {
                                    if (touchY < pop_layout.getTop() || touchY > pop_layout.getBottom()) {
                                        popupWindow.dismiss();
                                    }
                                } else if (builder.showAtLocationType.equals(GRAVITY_BOTTOM)) {
                                    if (touchY < pop_layout.getTop()) {
                                        popupWindow.dismiss();
                                    }
                                }
                            }
                            break;
                        default:
                            break;
                    }
                    return true;
                }
            });
        } else {
            throw new NullPointerException("---> BlurPopWin ---> initBlurPopWin --->builder=null");
        }
//        点击整个cardview
        pop_layout.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (builder.popupCallback != null) {
                    builder.popupCallback.onClick(BlurPopWin.this);
                }
            }
        });

        //点击电话

        logistics_InformationView.setOnPhoneClickListener(new LogisticsInformationView.OnPhoneClickListener() {
            @Override
            public void onPhoneClick(String phoneNumber) {

                if (builder.onPhoneClickListener != null) {
//                    Tools.showToast("点击电话了");
                    builder.onPhoneClickListener.onphoneclock(phoneNumber);

                }
            }
        });


        return this;
    }

    public static class Builder {

        private ArrayList logisticsDataList;
        protected BlurPopWin blurPopWin;
        protected int titleTextSize, contentTextSize;
        protected Activity activity;
        protected Context context;
        protected PopupCallback popupCallback;
        protected OnPhoneClickListener onPhoneClickListener;
        protected int radius;
        protected String title, content;
        protected boolean isCancelable;
        //默认不显示XX
        protected boolean isShowClose = false;
        protected boolean isBackgroundClose = true;
        protected String showAtLocationType = GRAVITY_CENTER;

        public Builder(@NonNull Context context) {
            this.activity = (Activity) context;
            this.context = context;
            this.isCancelable = true;

        }

        private void initData() {

        }

        public Builder onphoneclock(OnPhoneClickListener onPhoneClickListener) {
            this.onPhoneClickListener = onPhoneClickListener;
            return this;
        }

        public Builder onClick(PopupCallback popupCallback) {
            this.popupCallback = popupCallback;
            return this;
        }

        //设置list数据
        public Builder SetLogisticsDataList(ArrayList logisticsDataList) {
            this.logisticsDataList = logisticsDataList;
            return this;

        }

        /*
        * 设置标题
        * */
        public Builder setTitle(@StringRes int titleRes) {
            setTitle(this.context.getString(titleRes));
            return this;
        }

        //标题
        public Builder setTitle(@NonNull String title) {
            this.title = title;
            return this;
        }

        //圆角
        public Builder setRadius(int radius) {
            this.radius = radius;
            return this;
        }

        //标题字体大小
        public Builder setTitleTextSize(int size) {
            this.titleTextSize = size;
            return this;
        }

        //内容字体大小
        public Builder setContentTextSize(int size) {
            this.contentTextSize = size;
            return this;
        }

        /*
        * 设置主文内容
        * */
        public Builder setContent(@StringRes int contentRes) {
            setContent(this.context.getString(contentRes));
            return this;
        }

        /*
        * 设置主文内容
        * */
        public Builder setContent(@NonNull String content) {
            this.content = content;
            return this;
        }

        /*
        * 默认居中,手动设置了才在最下面
        * */
        public Builder setshowAtLocationType(int type) {
            if (type == 0) {
                this.showAtLocationType = GRAVITY_CENTER;
            } else if (type == 1) {
                this.showAtLocationType = GRAVITY_BOTTOM;
            }

            return this;
        }

        //是否显示close标志
        public Builder setShowCloseButton(@NonNull boolean flag) {
            this.isShowClose = flag;
            return this;
        }

        public Builder setOutSideClickable(@NonNull boolean flag) {
            this.isBackgroundClose = flag;
            return this;
        }

        @UiThread
        public BlurPopWin build() {
            return new BlurPopWin(this);
        }

        @UiThread
        public BlurPopWin show(View view) {
            BlurPopWin blurPopWin = build();
            blurPopWin.show(view);

            return blurPopWin;
        }

    }

    public interface PopupCallback {

        void onClick(@NonNull BlurPopWin blurPopWin);
    }

    public interface OnPhoneClickListener {
        void onphoneclock(String phoneNumber);

    }
}

这两个方法最重要的是关于物流的那个自定义的View



/**
 * Created by Administrator on 2017/9/8.
 */

public class LogisticsInformationView extends View {

    Context context;

    /**
     * 绘制工具:画笔
     */

    Paint paint;
    Paint mPaintPhone;
    TextPaint textPaintPhone;

    /**
     * 绘制参数
     */

    int interval;//间隔:本文中指代物流信息和物流时间的间隔

    float radius;//圆形半径

    int left = 40;//距离左边边距
    int top = 20;//距离顶部边距(相当于XML中的Margin或者Padding效果)

    int windowWidth;//获取屏幕的宽高,避免定义数据过长超出屏幕
    int windowHeight;

    float width;

    String phoneNumber;//电话号码
    int phoneNumberWidth;//11位电话号码宽度是固定的
    int phoneNumberHeight;//11位电话号码高度是固定的

    /**
     * 绘制界面参数
     */
    List<LogisticsData> logisticsDataList;

    List<Integer> heightList;//获取每一条文本所占的高度,这里默认时间就占一行

    int heightTotal = 0;//获取总高度,用于绘制当前界面所占的高

    /**
     * 使用Map记录存在电话号码的坐标用于点击事件的存储
     * 起始坐标以及截止坐标的记录
     */
    HashMap<Float, String> phoneYNumber = new HashMap<Float, String>();
    HashMap<Float, Float> stopYX = new HashMap<Float, Float>();
    List<Float> stopYList = new ArrayList<>();//用于快速取出初始化的值

    private int startColor = R.color.text_color_start;
    private int endColor = R.color.text_color_end;
    private int mViewWidth = 0;
    LinearGradient mLinearGradient;

    public LogisticsInformationView(Context context) {
        super(context);
        this.context = context;
        init();
    }

    public LogisticsInformationView(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.context = context;
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.LogisticsInformationView);
        width = typedArray.getDimension(R.styleable.LogisticsInformationView_width, 20);//获取XML中设置的高度(圆形半径)
        radius = typedArray.getDimension(R.styleable.LogisticsInformationView_radius, 10);
        //程序在运行时维护了一个 TypedArray的池,程序调用时,会向该池中请求一个实例
        //用完之后,调用 recycle() 方法来释放该实例,从而使其可被其他模块复用。所以一定要调用

        typedArray.recycle();
        init();
    }

    public LogisticsInformationView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        this.context = context;
        init();
    }

    /**
     * 初始化相关参数
     */
    private void init() {
        paint = new Paint(Paint.ANTI_ALIAS_FLAG);//用于绘制时抗锯齿
        paint.setColor(getResources().getColor(R.color.normalColor));

        mPaintPhone = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaintPhone.setColor(getResources().getColor(R.color.colorPrimaryDark));
        mPaintPhone.setTextSize(30.0F);
//电话1111字的颜色
        textPaintPhone = new TextPaint();
        textPaintPhone.setColor(getResources().getColor(R.color.colorPrimaryDark));
        textPaintPhone.setTextSize(30.0F);
        textPaintPhone.setAntiAlias(true);

//        interval = dip2px(context, 16);//间距

        WindowManager wm = (WindowManager) getContext()
                .getSystemService(Context.WINDOW_SERVICE);

        windowWidth = wm.getDefaultDisplay().getWidth();
        windowHeight = wm.getDefaultDisplay().getHeight();
//设置渐变色
        mViewWidth = getMeasuredWidth();//必须调用了mesure方法
        //渐变色
        mLinearGradient = new LinearGradient(0, 0, mViewWidth, 0,
                new int[]{getResources().getColor(startColor), getResources().getColor(endColor)},
                null, Shader.TileMode.REPEAT);

    }


    /**
     * 传递相关物流信息对象,这里的对象可以更改为你的自定义的对象
     */
    public void setLogisticsDataList(List<LogisticsData> logisticsDataList) {
        this.logisticsDataList = logisticsDataList;

        heightList = new ArrayList<>();
        TextPaint textPaint = new TextPaint();
        textPaint.setTextSize(35.0F);

        //获取模拟电话宽度数据
        StaticLayout phoneLayout = new StaticLayout("15555555555", textPaint, (int) (windowWidth * 0.8), ALIGN_NORMAL, 1.0F, 0.0F, true);
        phoneNumberWidth = (int) phoneLayout.getLineWidth(0);
        phoneNumberHeight = phoneLayout.getHeight();
        //计算每行字符所占的高度
        for (int i = 0; i < logisticsDataList.size(); i++) {
            StaticLayout layout = new StaticLayout((logisticsDataList.get(i)).getContext() + "", textPaint,
                    (int) (windowWidth * 0.8), ALIGN_NORMAL, 1.0F, 0.0F, true);
            heightList.add(layout.getHeight());
            heightTotal = heightTotal + layout.getHeight() + interval + (i == 1 ? top : top * 2);//获取总共高度
        }
    }


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (logisticsDataList == null || logisticsDataList.size() == 0)
            return;

        List data = logisticsDataList;
        canvas.drawRect(left * 2, top, width + left * 2, heightTotal + top, paint);
        Paint mPaint = new Paint();
        TextPaint textPaint1 = new TextPaint();
        for (int i = 0; i < logisticsDataList.size(); i++) {
            if (i == 0) {
                mPaint.setAntiAlias(true);
                mPaint.setColor(getResources().getColor(R.color.checkColor));
                mPaint.setTextSize(30);
                TextPaint datePaint = new TextPaint();
                datePaint.setColor(getResources().getColor(R.color.checkColor));
                datePaint.setTextSize(20);
                datePaint.setAntiAlias(true);
                datePaint.measureText(((LogisticsData) data.get(i)).getDate());

                TextPaint texttimePaint = new TextPaint();
                texttimePaint.setColor(getResources().getColor(R.color.checkColor));
                texttimePaint.setTextSize(20);
                texttimePaint.setAntiAlias(true);
                texttimePaint.measureText(((LogisticsData) data.get(i)).getTime());
                canvas.drawText(((LogisticsData) data.get(i)).getDate() + "", 5, top + 32, datePaint);
                canvas.drawText(((LogisticsData) data.get(i)).getTime() + "", 5, top + 8, texttimePaint);

                TextPaint textPaint = new TextPaint();
                textPaint.setColor(getResources().getColor(R.color.checkColor));
                textPaint.setTextSize(30.0F);
                textPaint.setAntiAlias(true);


                mPaint.setShader(mLinearGradient);
                canvas.drawCircle(width / 2 + left * 2, top + 5, radius * 2 + 2, mPaint);

                textPaint1.setColor(getResources().getColor(R.color.white));
                textPaint1.setTextSize(26);
                textPaint1.setAntiAlias(true);
                textPaint1.measureText("收");
                canvas.drawText("收", left * 2 - 12, top + 14, textPaint1);
                String[] splitData = splitString(((LogisticsData) data.get(i)).getContext() + "");
                if (splitData != null) {
                    splitPhoneData(splitData, canvas, textPaint, 0, true);
                } else {
                    StaticLayout layout = new StaticLayout(((LogisticsData) data.get(i)).getContext() + "", textPaint,
                            (int) (windowWidth * 0.7), ALIGN_NORMAL, 1.0F, 0.0F, true);
                    canvas.save();
                    canvas.translate(left * 2 + radius * 2 + 10, 0);
                    layout.draw(canvas);
                    canvas.restore();
                }
            } else {
                int heightData = 0;
                for (int j = 0; j < i; j++) {
                    heightData = heightData + heightList.get(j) + interval + (j == 0 ? top : top * 2);
                }
                paint.setColor(getResources().getColor(R.color.normalColor));
                canvas.drawCircle(width / 2 + left * 2, heightData + 44, radius * 2 + 2, mPaint);
                canvas.drawText("运", +left * 2 - 10, heightData + 44 + 8, textPaint1);

                paint.setTextSize(30);
                TextPaint datePaint = new TextPaint();
                datePaint.setColor(getResources().getColor(R.color.normalColor));
                datePaint.setTextSize(20);
                datePaint.setAntiAlias(true);
                datePaint.measureText(((LogisticsData) data.get(i)).getDate());

                TextPaint texttimePaint = new TextPaint();
                texttimePaint.setColor(getResources().getColor(R.color.normalColor));
                texttimePaint.setTextSize(20);
                texttimePaint.setAntiAlias(true);
                texttimePaint.measureText(((LogisticsData) data.get(i)).getTime());
                canvas.drawText(((LogisticsData) data.get(i)).getDate() + "", 5,  20 + heightData + top, datePaint);
                canvas.drawText(((LogisticsData) data.get(i)).getTime() + "", 5, 44 + heightData + top, texttimePaint);
                TextPaint textPaint = new TextPaint();
                textPaint.setColor(getResources().getColor(R.color.normalColor));
                textPaint.setTextSize(30.0F);
                textPaint.setAntiAlias(true);
                String[] splitData = splitString(((LogisticsData) data.get(i)).getContext() + "");
                if (splitData != null) {
                    splitPhoneData(splitData, canvas, textPaint, heightData, false);
                } else {
                    StaticLayout layout = new StaticLayout(((LogisticsData) data.get(i)).getContext() + "", textPaint, (int) (windowWidth * 0.7), ALIGN_NORMAL, 1.0F, 0.0F, true);
                    canvas.save();
                    canvas.translate(left * 2 + radius * 2 + 10, heightData + top);
                    layout.draw(canvas);
                    canvas.restore();
                }

            }
        }
    }

    //onmesure方法
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //当没有数据的时候截断绘制过程,当set数据进来的时候还是会走这里绘制图形
        if (logisticsDataList == null || logisticsDataList.size() == 0)
            return;
        //这里绘制所需的宽高
        setMeasuredDimension(widthMeasureSpec, heightTotal + top);
    }


    /**
     * 根据手机的分辨率从 dp 的单位 转成为 px(像素)
     */
    public static int dip2px(Context context, float dpValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (dpValue * scale + 0.5f);
    }

    public String[] splitString(String data) {
        String initData = getPhoneNumber(data);
        phoneNumber = initData;
        if (initData != null) {
            //说明有电话号码,进行数据分割,默认每一段content中最多包含一个手机号码
            String[] splitData = data.split(initData);
            return splitData;
        }
        //没有电话号码就返回null
        return null;
    }


    /**
     * 获取字符串中是否包含11位的手机号码
     */
    public String getPhoneNumber(String numer) {
        char[] temp = numer.toCharArray();
        String value = "";
        int licz = 0;

        for (int i = 0; i < temp.length; i++) {
            if (licz < 11) {
                if (Character.toString(temp[i]).matches("[0-9]")) {
                    value += Character.toString(temp[i]);
                    licz++;
                } else if (Character.toString(temp[i]).matches("\u0020|\\-|\\(|\\)")) {

                } else {
                    value = "";
                    licz = 0;
                }
            }
        }

        if (value.length() != 11) {
            value = null;
        } else {
            value = value.trim();
        }
//        LogUtil.e("手机号码=" + value);
        return value;
    }

    /**
     * 逻辑操作电话号码变色可点
     */
    public void splitPhoneData(String[] splitData, Canvas canvas, TextPaint textPaint, int heightData, boolean isTop) {
        StaticLayout layoutPhone = new StaticLayout(phoneNumber, textPaintPhone, (int) (windowWidth * 0.7), ALIGN_NORMAL, 1.0F, 0.0F, true);

        //1.首先绘制电话前段的数据
        StaticLayout layout = new StaticLayout(splitData[0], textPaint, (int) (windowWidth * 0.7), ALIGN_NORMAL, 1.0F, 0.0F, true);
        int layoutFirst = layout.getHeight();
        canvas.save();//很重要,不然会样式出错
        canvas.translate(left * 2 + radius * 2 + 10, heightData + (isTop ? 0 : top));
        layout.draw(canvas);
        canvas.restore();//重置

        //判断截取端是多少
        if (splitData.length <= 1) {
            //没有后续
            //2.判断是剩下的宽度是否能够容纳手机号码宽度
            if ((int) (windowWidth * 0.7) - layout.getLineWidth(layout.getLineCount() - 1) > phoneNumberWidth) {
                phoneYNumber.put((float) (heightData + (isTop ? -10 : 10) + layoutPhone.getHeight()), phoneNumber);
                stopYX.put((float) (heightData + (isTop ? -10 : 10) + layoutPhone.getHeight()), left * 2 + radius * 2 + 10 + (layout.getLineWidth(layout.getLineCount() - 1)) + phoneNumberWidth);
                stopYList.add((float) (heightData + (isTop ? -10 : 10) + layoutPhone.getHeight()));
                mPaintPhone.setColor(getResources().getColor(R.color.colorPrimaryDark));
                canvas.drawText(phoneNumber, left * 2 + radius * 2 + 10 + (layout.getLineWidth(layout.getLineCount() - 1)), layoutFirst + heightData + (isTop ? -10 : 10), mPaintPhone);
            } else {
                phoneYNumber.put((float) (layoutFirst + heightData + (isTop ? 0 : top) + layoutPhone.getHeight()), phoneNumber);
                stopYX.put((float) (layoutFirst + heightData + (isTop ? 0 : top) + layoutPhone.getHeight()), left * 2 + radius * 2 + 10 + phoneNumberWidth);
                stopYList.add((float) (layoutFirst + heightData + (isTop ? 0 : top) + layoutPhone.getHeight()));
                //5.1获取之前的高度
                canvas.save();//很重要,不然会样式出错
                canvas.translate(left * 2 + radius * 2 + 10, layoutFirst + heightData + (isTop ? 0 : top));
                layoutPhone.draw(canvas);
                canvas.restore();//重置
            }
        } else {
            //有后续
            //获取最后一段的宽度
            StaticLayout layoutLast = new StaticLayout(splitData[1], textPaint, (int) (windowWidth * 0.7), ALIGN_NORMAL, 1.0F, 0.0F, true);

            //2.判断是剩下的宽度是否能够容纳手机号码宽度
            if ((int) (windowWidth * 0.7) - layout.getLineWidth(layout.getLineCount() - 1) > phoneNumberWidth) {
                //3.1.如果是可以容纳的情况
                //记录Map坐标轴数据
                phoneYNumber.put((float) (heightData + (isTop ? -10 : 10) + layoutPhone.getHeight()), phoneNumber);
                stopYX.put((float) (heightData + (isTop ? -10 : 10) + layoutPhone.getHeight()), left * 2 + radius * 2 + 10 + (layout.getLineWidth(layout.getLineCount() - 1)) + phoneNumberWidth);
                stopYList.add((float) (heightData + (isTop ? -10 : 10) + layoutPhone.getHeight()));
                mPaintPhone.setColor(getResources().getColor(R.color.colorPrimaryDark));
                canvas.drawText(phoneNumber, left * 2 + radius * 2 + 10 + (layout.getLineWidth(layout.getLineCount() - 1)), layoutFirst + heightData + (isTop ? -10 : 10), mPaintPhone);
                if ((int) (windowWidth * 0.7) - layout.getLineWidth(layout.getLineCount() - 1) - phoneNumberWidth > layoutLast.getLineWidth(0)) {
                    //4.1.如果一行就可以绘制完成
                    mPaintPhone.setColor(getResources().getColor(isTop ? R.color.checkColor : R.color.normalColor));

                    canvas.drawText(splitData[1], left * 2 + radius * 2 + 10 + (layout.getLineWidth(layout.getLineCount() - 1) + phoneNumberWidth), layoutFirst + heightData + (isTop ? -10 : 10), mPaintPhone);
                } else {
                    //4.2.一行不可以完成的情况下继续分割成两份,一份是drawText拼接到最后,一份是StaticLayout另起一行绘制
                    double percentLast = (((int) (windowWidth * 0.7) - layout.getLineWidth(layout.getLineCount() - 1) - phoneNumberWidth));
                    //获取字符能显示的最大Length
                    double maxLength = percentLast / 30.0F;//这里的35.0是一个中文字体的大小
                    String lastStringPre = splitData[1].substring(0, (int) maxLength + 1);//获取最大数据的长度的字符串拼接
                    String lastStringLas = splitData[1].substring((int) maxLength + 1);
                    mPaintPhone.setColor(getResources().getColor(isTop ? R.color.checkColor : R.color.normalColor));
                    canvas.drawText(lastStringPre, left * 2 + radius * 2 + 10 + (layout.getLineWidth(layout.getLineCount() - 1) + phoneNumberWidth), layoutFirst + heightData + (isTop ? -10 : 10), mPaintPhone);

                    //另起一行写剩余的数据

                    StaticLayout layoutlastString = new StaticLayout(lastStringLas, textPaint, (int) (windowWidth * 0.7), ALIGN_NORMAL, 1.0F, 0.0F, true);
                    canvas.save();//很重要,不然会样式出错
                    canvas.translate(left * 2 + radius * 2 + 10, layoutFirst + heightData + (isTop ? 0 : top));
                    layoutlastString.draw(canvas);
                    canvas.restore();//重置
                }
            } else {
                //3.2.如果是不可以容纳的情况,现在默认电话号码不可以容纳的情况就另起一行使用StaticLayout绘制
                phoneYNumber.put((float) (layoutFirst + heightData + (isTop ? 0 : top) + layoutPhone.getHeight()), phoneNumber);
                stopYX.put((float) (layoutFirst + heightData + (isTop ? 0 : top) + layoutPhone.getHeight()), left * 2 + radius * 2 + 10 + phoneNumberWidth);
                stopYList.add((float) (layoutFirst + heightData + (isTop ? 0 : top) + layoutPhone.getHeight()));
                //5.1获取之前的高度
                canvas.save();//很重要,不然会样式出错
                canvas.translate(left * 2 + radius * 2 + 10, layoutFirst + heightData + (isTop ? 0 : top));
                layoutPhone.draw(canvas);
                canvas.restore();//重置
                if ((int) (windowWidth * 0.7) - phoneNumberWidth > layoutLast.getLineWidth(0)) {
                    //4.1.如果一行就可以绘制完成
                    canvas.drawText(splitData[1], left * 2 + radius * 2 + 10 + phoneNumberWidth, layoutPhone.getHeight() + layoutFirst + heightData + (isTop ? -10 : 10), mPaintPhone);
                } else {
                    //4.2.一行不可以完成的情况下继续分割成两份,一份是drawText拼接到最后,一份是StaticLayout另起一行绘制
                    double percentLast = (int) (windowWidth * 0.7) - phoneNumberWidth;
                    //获取字符能显示的最大Length
                    double maxLength = percentLast / 30.0F;//这里的35.0是一个中文字体的大小

                    String lastStringPre = splitData[1].substring(0, (int) maxLength + 1);//获取最大数据的长度的字符串拼接
                    String lastStringLas = splitData[1].substring((int) maxLength + 1);
                    mPaintPhone.setColor(getResources().getColor(isTop ? R.color.checkColor : R.color.normalColor));
                    canvas.drawText(lastStringPre, left * 2 + radius * 2 + 10 + phoneNumberWidth, layoutPhone.getHeight() + layoutFirst + heightData + (isTop ? -10 : 10), mPaintPhone);

                    //另起一行写剩余的数据

                    StaticLayout layoutlastString = new StaticLayout(lastStringLas, textPaint, (int) (windowWidth * 0.7), ALIGN_NORMAL, 1.0F, 0.0F, true);
                    canvas.save();//很重要,不然会样式出错
                    canvas.translate(left * 2 + radius * 2 + 10, layoutPhone.getHeight() + layoutFirst + heightData + (isTop ? 0 : top));
                    layoutlastString.draw(canvas);
                    canvas.restore();//重置
                }
            }

        }
    }


    /**
     * 实现电话号码的点击事件
     */
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                //按下(这里默认点击一下就弹出Dialog样式)
                float x = event.getX();
                float y = event.getY();
                //判断点击的范围类型
                for (int i = 0; i < stopYList.size(); i++) {
                    if (y < stopYList.get(i) && (stopYList.get(i) - y) <= phoneNumberHeight
                            && x < stopYX.get(stopYList.get(i)) && (stopYX.get(stopYList.get(i)) - x) <= phoneNumberWidth) {
                        //成立,获取X轴的相关信息
                        if (mOnPhoneClickListener != null) {
                            mOnPhoneClickListener.onPhoneClick(phoneYNumber.get(stopYList.get(i)));
                        }

                        break;
                    }
                }

                break;
            case MotionEvent.ACTION_UP:
                //抬起
                break;
            case MotionEvent.ACTION_MOVE:
                //取消
                break;
            case MotionEvent.ACTION_CANCEL:
                break;
            default:
                break;
        }


        return super.onTouchEvent(event);
    }

    /**
     * 提供接口
     */
    private OnPhoneClickListener mOnPhoneClickListener;

    public interface OnPhoneClickListener {
        void onPhoneClick(String phoneNumber);
    }

    public void setOnPhoneClickListener(OnPhoneClickListener l) {
        mOnPhoneClickListener = l;
    }

}

详情请查看demo https://github.com/newsupercode/WuliuNodeView
这个demo参考了:https://github.com/aesion/NodeProgressView

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

葫芦娃你好我是皮卡丘

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值