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