From bcec91f4486a17f46e1bd51895d04575ccd23fb3 Mon Sep 17 00:00:00 2001 From: shell Date: Sat, 12 May 2018 23:48:48 +0800 Subject: [PATCH] rebase tagViewGroup by kotlin --- build.gradle | 2 + gradle.properties | 17 +- library/build.gradle | 11 + .../java/com/licrafter/tagview/DIRECTION.java | 50 -- .../com/licrafter/tagview/TagAdapter.java | 63 -- .../com/licrafter/tagview/TagViewGroup.java | 738 ------------------ .../tagview/utils/DipConvertUtils.java | 15 - .../com/licrafter/tagview/views/ITagView.java | 32 - .../licrafter/tagview/views/RippleView.java | 110 --- .../licrafter/tagview/views/TagTextView.java | 45 -- .../kotlin/com/licrafter/tagview/DIRECTION.kt | 19 + .../com/licrafter/tagview/TagAdapter.kt | 61 ++ .../com/licrafter/tagview/TagViewGroup.kt | 738 ++++++++++++++++++ .../tagview/utils/DipConvertUtils.kt | 16 + .../com/licrafter/tagview/views/ITagView.kt | 31 + .../com/licrafter/tagview/views/RippleView.kt | 96 +++ .../licrafter/tagview/views/TagTextView.kt | 34 + .../com/licrafter/sample/TagEditActivity.java | 4 - .../com/licrafter/sample/utils/DataRepo.java | 8 +- .../sample/utils/DirectionUtils.java | 46 ++ .../licrafter/sample/views/TagEditDialog.java | 14 +- .../licrafter/sample/views/TagImageView.java | 5 +- 22 files changed, 1076 insertions(+), 1079 deletions(-) delete mode 100644 library/src/main/java/com/licrafter/tagview/DIRECTION.java delete mode 100644 library/src/main/java/com/licrafter/tagview/TagAdapter.java delete mode 100644 library/src/main/java/com/licrafter/tagview/TagViewGroup.java delete mode 100644 library/src/main/java/com/licrafter/tagview/utils/DipConvertUtils.java delete mode 100644 library/src/main/java/com/licrafter/tagview/views/ITagView.java delete mode 100644 library/src/main/java/com/licrafter/tagview/views/RippleView.java delete mode 100644 library/src/main/java/com/licrafter/tagview/views/TagTextView.java create mode 100644 library/src/main/kotlin/com/licrafter/tagview/DIRECTION.kt create mode 100644 library/src/main/kotlin/com/licrafter/tagview/TagAdapter.kt create mode 100644 library/src/main/kotlin/com/licrafter/tagview/TagViewGroup.kt create mode 100644 library/src/main/kotlin/com/licrafter/tagview/utils/DipConvertUtils.kt create mode 100644 library/src/main/kotlin/com/licrafter/tagview/views/ITagView.kt create mode 100644 library/src/main/kotlin/com/licrafter/tagview/views/RippleView.kt create mode 100644 library/src/main/kotlin/com/licrafter/tagview/views/TagTextView.kt create mode 100644 sample/src/main/java/com/licrafter/sample/utils/DirectionUtils.java diff --git a/build.gradle b/build.gradle index 648a5ea..c29279e 100644 --- a/build.gradle +++ b/build.gradle @@ -1,12 +1,14 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { + ext.kotlin_version = '1.2.30' repositories { jcenter() google() } dependencies { classpath 'com.android.tools.build:gradle:3.0.1' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" // NOTE: Do not place your application dependence.gradle here; they belong // in the individual module build.gradle files diff --git a/gradle.properties b/gradle.properties index aac7c9b..2fb13b6 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,17 +1,16 @@ -# Project-wide Gradle settings. - -# IDE (e.g. Android Studio) users: -# Gradle settings configured through the IDE *will override* -# any settings specified in this file. - +## Project-wide Gradle settings. +# # For more details on how to configure your build environment visit # http://www.gradle.org/docs/current/userguide/build_environment.html - +# # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. -org.gradle.jvmargs=-Xmx1536m - +# Default value: -Xmx1024m -XX:MaxPermSize=256m +# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 +# # When configured, Gradle will run in incubating parallel mode. # This option should only be used with decoupled projects. More details, visit # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects # org.gradle.parallel=true +#Sun Apr 22 13:39:24 CST 2018 +org.gradle.jvmargs=-Xmx1536m diff --git a/library/build.gradle b/library/build.gradle index 90569c8..88e10db 100644 --- a/library/build.gradle +++ b/library/build.gradle @@ -1,4 +1,5 @@ apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' apply from: new File('../dependence.gradle') android { @@ -20,6 +21,12 @@ android { proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } + sourceSets { + main { + resources.srcDirs = ['src/main/resources', 'src/main/resources/'] + java.srcDirs = ['src/main/java', 'src/main/kotlin'] + } + } } dependencies { @@ -29,4 +36,8 @@ dependencies { }) api "com.android.support:appcompat-v7:${project.ext.supportLib}" testCompile "junit:junit:${project.ext.junit}" + compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" +} +repositories { + mavenCentral() } diff --git a/library/src/main/java/com/licrafter/tagview/DIRECTION.java b/library/src/main/java/com/licrafter/tagview/DIRECTION.java deleted file mode 100644 index 1686c5d..0000000 --- a/library/src/main/java/com/licrafter/tagview/DIRECTION.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.licrafter.tagview; - -/** - * Created by lijx on 2016/12/20. - */ - -public enum DIRECTION { - LEFT_CENTER(8), LEFT_TOP(10), LEFT_TOP_TILT(9), LEFT_BOTTOM(6), LEFT_BOTTOM_TILT(7), - RIGHT_CENTER(3), RIGHT_TOP(1), RIGHT_TOP_TILT(2), RIGHT_BOTTOM(5), RIGHT_BOTTOM_TILT(4), - CENTER(0); - - private int value; - - DIRECTION(int value) { - this.value = value; - } - - public int getValue() { - return value; - } - - public static DIRECTION valueOf(int value) { - switch (value) { - case 0: - return CENTER; - case 1: - return RIGHT_TOP; - case 2: - return RIGHT_TOP_TILT; - case 3: - return RIGHT_CENTER; - case 4: - return RIGHT_BOTTOM_TILT; - case 5: - return RIGHT_BOTTOM; - case 6: - return LEFT_BOTTOM; - case 7: - return LEFT_BOTTOM_TILT; - case 8: - return LEFT_CENTER; - case 9: - return LEFT_TOP_TILT; - case 10: - return LEFT_TOP; - default: - return CENTER; - } - } -} diff --git a/library/src/main/java/com/licrafter/tagview/TagAdapter.java b/library/src/main/java/com/licrafter/tagview/TagAdapter.java deleted file mode 100644 index 348a12f..0000000 --- a/library/src/main/java/com/licrafter/tagview/TagAdapter.java +++ /dev/null @@ -1,63 +0,0 @@ -package com.licrafter.tagview; - -import android.database.DataSetObservable; -import android.database.DataSetObserver; -import android.util.Log; -import android.view.View; -import android.view.ViewGroup; - -import com.licrafter.tagview.views.ITagView; - -/** - * Created by lijx on 2017/5/17. - */ - -public abstract class TagAdapter { - - private static final boolean DEBUG = false; - private static final String TAG = "TagAdapter"; - private DataSetObservable mObservable = new DataSetObservable(); - - /** - * Return the number of ITagView. - */ - public abstract int getCount(); - - /** - * Return the ITagView associated with a specified position. - */ - public abstract ITagView getItem(int position); - - public ITagView instantiateItem(ViewGroup container, int position) { - long itemId = getItemId(position); - ITagView tagView = getItem(position); - if (DEBUG) Log.v(TAG, "Adding tagView #" + itemId + ": TagView=" + tagView); - container.addView((View) tagView); - return tagView; - } - - public boolean isViewFromObject(View view, Object object) { - return object.equals(view); - } - - public void destroyItem(ViewGroup container, int position, ITagView object) { - - } - - public int getItemId(int position) { - return position; - } - - public void notifyDataSetChanged() { - mObservable.notifyChanged(); - } - - public void registerDataSetObserver(DataSetObserver observer) { - mObservable.registerObserver(observer); - } - - public void unregisterDataSetObserver(DataSetObserver observer) { - mObservable.unregisterObserver(observer); - } - -} diff --git a/library/src/main/java/com/licrafter/tagview/TagViewGroup.java b/library/src/main/java/com/licrafter/tagview/TagViewGroup.java deleted file mode 100644 index 7ae029b..0000000 --- a/library/src/main/java/com/licrafter/tagview/TagViewGroup.java +++ /dev/null @@ -1,738 +0,0 @@ -package com.licrafter.tagview; - -import android.animation.Animator; -import android.content.Context; -import android.content.res.Resources; -import android.content.res.TypedArray; -import android.database.DataSetObserver; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -import android.graphics.Path; -import android.graphics.PathMeasure; -import android.graphics.RectF; -import android.support.v4.view.GestureDetectorCompat; -import android.util.AttributeSet; -import android.util.Property; -import android.view.GestureDetector; -import android.view.MotionEvent; -import android.view.View; -import android.view.ViewGroup; - -import com.licrafter.tagview.utils.DipConvertUtils; -import com.licrafter.tagview.views.ITagView; -import com.licrafter.tagview.views.RippleView; - -import java.util.ArrayList; -import java.util.List; - - -/** - * author: shell - * date 2016/12/20 下午2:24 - **/ -public class TagViewGroup extends ViewGroup { - - public static final int DEFAULT_RADIUS = 8;//默认外圆半径 - public static final int DEFAULT_INNER_RADIUS = 4;//默认内圆半径 - public static final int DEFAULT_V_DISTANCE = 28;//默认竖直(上/下)方向线条长度 - public static final int DEFAULT_TILT_DISTANCE = 20;//默认斜线长度 - public static final int DEFAULT_LINES_WIDTH = 1;//默认线宽 - public static final int DEFAULT_MAX_TAG = 6;//默认标签最大数量 - private static final int DEFAULT_RIPPLE_MAX_RADIUS = 20;//水波纹默认最大半径 - private static final int DEFULT_RIPPLE_ALPHA = 100;//默认水波纹透明度 - - static class ItemInfo { - ITagView item; - int position; - RectF rectF = new RectF(); - } - - private Paint mPaint; - private Path mPath; - private Path mDstPath; - private PathMeasure mPathMeasure; - private Animator mShowAnimator; - private Animator mHideAnimator; - private TagAdapter mAdapter; - private GestureDetectorCompat mGestureDetector; - private OnTagGroupClickListener mClickListener; - private OnTagGroupDragListener mScrollListener; - private final TagSetObserver mObserver = new TagSetObserver(); - - private RippleView mRippleView; - private int mRippleMaxRadius;//水波纹最大半径 - private int mRippleMinRadius;//水波纹最小半径 - private int mRippleAlpha;//水波纹起始透明度 - private int mRadius;//外圆半径 - private int mInnerRadius;//内圆半径 - private int mTDistance;//斜线长度 - private int mVDistance;//竖直(上/下)方向线条长度 - private float mTagAlpha;//Tag标签的透明度 - private RectF mCenterRect; - private ArrayList mItems = new ArrayList<>(); - private int[] mChildUsed; - private int mCenterX;//圆心 X 坐标 - private int mCenterY;//圆心 Y 坐标 - private float mPercentX; - private float mPercentY; - private int mLinesWidth;//线条宽度 - - private float mLinesRatio = 1; - - public TagViewGroup(Context context) { - this(context, null); - } - - public TagViewGroup(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public TagViewGroup(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - Resources.Theme theme = context.getTheme(); - TypedArray array = theme.obtainStyledAttributes(attrs, R.styleable.TagViewGroup, defStyleAttr, 0); - mRadius = array.getDimensionPixelSize(R.styleable.TagViewGroup_radius, DipConvertUtils.dip2px(context, DEFAULT_RADIUS)); - mInnerRadius = array.getDimensionPixelSize(R.styleable.TagViewGroup_inner_radius, DipConvertUtils.dip2px(context, DEFAULT_INNER_RADIUS)); - mTDistance = array.getDimensionPixelSize(R.styleable.TagViewGroup_tilt_distance, DipConvertUtils.dip2px(context, DEFAULT_TILT_DISTANCE)); - mVDistance = array.getDimensionPixelSize(R.styleable.TagViewGroup_v_distance, DipConvertUtils.dip2px(context, DEFAULT_V_DISTANCE)); - mLinesWidth = array.getDimensionPixelSize(R.styleable.TagViewGroup_line_width, DipConvertUtils.dip2px(context, DEFAULT_LINES_WIDTH)); - mRippleMaxRadius = array.getDimensionPixelSize(R.styleable.TagViewGroup_ripple_maxRadius, DipConvertUtils.dip2px(context, DEFAULT_RIPPLE_MAX_RADIUS)); - mRippleAlpha = array.getInteger(R.styleable.TagViewGroup_ripple_alpha, DEFULT_RIPPLE_ALPHA); - mRippleMinRadius = mInnerRadius + (mRadius - mInnerRadius) / 2; - array.recycle(); - mPaint = new Paint(); - mPath = new Path(); - mDstPath = new Path(); - mPathMeasure = new PathMeasure(); - mPaint.setAntiAlias(true); - mGestureDetector = new GestureDetectorCompat(context, new TagOnGestureListener()); - mChildUsed = new int[4]; - mCenterRect = new RectF(); - } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - measureChildren(widthMeasureSpec, heightMeasureSpec); - mChildUsed = getChildUsed(); - //园中心默认在左上角 (0,0) - mCenterX = (int) (getMeasuredWidth() * mPercentX); - mCenterY = (int) (getMeasuredHeight() * mPercentY); - checkBounds(); - mCenterRect.set(mCenterX - mRadius, mCenterY - mRadius, mCenterX + mRadius, mCenterY + mRadius); - if (mRippleView != null) { - mRippleView.setCenterPoint(mCenterX, mCenterY); - } - } - - /** - * 获取中心圆上下左右各个方向的宽度 - * - * @return int[]{left,top,right,bottom} - */ - private int[] getChildUsed() { - int childCount = getChildCount(); - int leftMax = mVDistance, topMax = mVDistance, rightMax = mVDistance, bottomMax = mVDistance; - - for (int i = 0; i < childCount; i++) { - ITagView child = (ITagView) getChildAt(i); - switch (child.getDirection()) { - case RIGHT_TOP_TILT://右上斜线 - rightMax = Math.max(rightMax, mTDistance + child.getMeasuredWidth()); - topMax = Math.max(topMax, child.getMeasuredHeight() + mTDistance); - break; - case RIGHT_TOP://右上 - rightMax = Math.max(rightMax, child.getMeasuredWidth()); - topMax = Math.max(topMax, child.getMeasuredHeight() + mVDistance); - break; - case RIGHT_CENTER://右中 - rightMax = Math.max(rightMax, child.getMeasuredWidth()); - topMax = Math.max(topMax, Math.max(mVDistance, child.getMeasuredHeight())); - break; - case RIGHT_BOTTOM://右下 - rightMax = Math.max(rightMax, child.getMeasuredWidth()); - bottomMax = mVDistance; - break; - case RIGHT_BOTTOM_TILT: - rightMax = Math.max(rightMax, mTDistance + child.getMeasuredWidth()); - bottomMax = mTDistance; - break; - case LEFT_TOP://左上 - leftMax = Math.max(leftMax, child.getMeasuredWidth()); - topMax = Math.max(topMax, child.getMeasuredHeight() + mVDistance); - break; - case LEFT_TOP_TILT://左上斜线 - leftMax = Math.max(leftMax, child.getMeasuredWidth() + mTDistance); - topMax = Math.max(topMax, child.getMeasuredHeight() + mTDistance); - break; - case LEFT_CENTER://左中 - leftMax = Math.max(leftMax, child.getMeasuredWidth()); - topMax = Math.max(topMax, Math.max(mVDistance, child.getMeasuredHeight())); - break; - case LEFT_BOTTOM://左下 - leftMax = Math.max(leftMax, child.getMeasuredWidth()); - bottomMax = mVDistance; - break; - case LEFT_BOTTOM_TILT://左下斜线 - leftMax = Math.max(leftMax, child.getMeasuredWidth() + mTDistance); - bottomMax = mTDistance; - break; - } - - } - return new int[]{leftMax, topMax, rightMax, bottomMax}; - } - - private void checkBounds() { - int rightAvailable = getMeasuredWidth() - mCenterX; - int leftAvailable = mCenterX; - if (mChildUsed[2] > rightAvailable) { - mCenterX -= (mChildUsed[2] - rightAvailable); - } - if (mChildUsed[0] > leftAvailable) { - mCenterX += (mChildUsed[0] - leftAvailable); - } - } - - @Override - protected void onLayout(boolean changed, int l, int t, int r, int b) { - int left = 0, top = 0; - for (int i = 0; i < getChildCount(); i++) { - ITagView child = (ITagView) getChildAt(i); - switch (child.getDirection()) { - case RIGHT_TOP_TILT://右上斜线 - top = mCenterY - mTDistance - child.getMeasuredHeight(); - left = mCenterX + mTDistance; - break; - case RIGHT_TOP://右上 - left = mCenterX; - top = mCenterY - mVDistance - child.getMeasuredHeight(); - break; - case RIGHT_CENTER://右中 - left = mCenterX; - top = mCenterY - child.getMeasuredHeight(); - break; - case RIGHT_BOTTOM://右下 - left = mCenterX; - top = mVDistance + mCenterY - child.getMeasuredHeight(); - break; - case RIGHT_BOTTOM_TILT://右下斜线 - left = mCenterX + mTDistance; - top = mTDistance + mCenterY - child.getMeasuredHeight(); - break; - case LEFT_TOP://左上 - left = mCenterX - child.getMeasuredWidth(); - top = mCenterY - mVDistance - child.getMeasuredHeight(); - break; - case LEFT_TOP_TILT://左上斜线 - left = mCenterX - child.getMeasuredWidth() - mTDistance; - top = mCenterY - mTDistance - child.getMeasuredHeight(); - break; - case LEFT_CENTER://左中 - left = mCenterX - child.getMeasuredWidth(); - top = mCenterY - child.getMeasuredHeight(); - break; - case LEFT_BOTTOM://左下 - left = mCenterX - child.getMeasuredWidth(); - top = mVDistance + mCenterY - child.getMeasuredHeight(); - break; - case LEFT_BOTTOM_TILT://左下斜线 - left = mCenterX - child.getMeasuredWidth() - mTDistance; - top = mTDistance + mCenterY - child.getMeasuredHeight(); - break; - case CENTER: - left = 0; - top = 0; - break; - } - child.layout(left, top, left + child.getMeasuredWidth(), top + child.getMeasuredHeight()); - refreshTagsInfo(child); - } - } - - @Override - protected void dispatchDraw(Canvas canvas) { - super.dispatchDraw(canvas); - //绘制折线 - drawLines(canvas); - //绘制外圆 - mPaint.setStyle(Paint.Style.FILL); - mPaint.setColor(Color.parseColor("#30000000")); - canvas.drawCircle(mCenterX, mCenterY, mRadius, mPaint); - //绘制内圆 - mPaint.setStyle(Paint.Style.FILL); - mPaint.setColor(Color.WHITE); - canvas.drawCircle(mCenterX, mCenterY, mInnerRadius, mPaint); - } - - @Override - protected void onAttachedToWindow() { - if (mRippleView != null) { - mRippleView.startRipple(); - } - super.onAttachedToWindow(); - } - - @Override - protected void onDetachedFromWindow() { - if (mRippleView != null) { - mRippleView.stopRipple(); - } - super.onDetachedFromWindow(); - } - - private void drawTagAlpha(float alpha) { - for (int i = 0; i < getChildCount(); i++) { - getChildAt(i).setAlpha(alpha); - } - } - - private void drawLines(Canvas canvas) { - mPaint.setColor(Color.WHITE); - mPaint.setStrokeWidth(mLinesWidth); - mPaint.setStyle(Paint.Style.STROKE); - - for (int i = 0; i < getChildCount(); i++) { - ITagView child = (ITagView) getChildAt(i); - mPath.reset(); - mPath.moveTo(mCenterX, mCenterY); - mDstPath.reset(); - mDstPath.rLineTo(0, 0); - switch (child.getDirection()) { - case RIGHT_TOP://右上 - case RIGHT_BOTTOM://右下 - case RIGHT_TOP_TILT://右上斜线 - case RIGHT_BOTTOM_TILT://右下斜线 - mPath.lineTo(child.getLeft(), child.getBottom()); - case RIGHT_CENTER://右中 - mPath.lineTo(child.getRight(), child.getBottom()); - break; - case LEFT_TOP://左上 - case LEFT_TOP_TILT://左上斜线 - case LEFT_BOTTOM://左下 - case LEFT_BOTTOM_TILT://左下斜线 - mPath.lineTo(child.getRight(), child.getBottom()); - case LEFT_CENTER://左中 - mPath.lineTo(child.getLeft(), child.getBottom()); - break; - } - mPathMeasure.setPath(mPath, false); - mPathMeasure.getSegment(0, mPathMeasure.getLength() * mLinesRatio, mDstPath, true); - canvas.drawPath(mDstPath, mPaint); - } - } - - ItemInfo addnewItem(int position) { - ItemInfo itemInfo = new ItemInfo(); - itemInfo.position = position; - itemInfo.item = mAdapter.instantiateItem(this, position); - mItems.add(itemInfo); - return itemInfo; - } - - /** - * 得到 TagViewGroup 中的所有标签列表 - * - * @return - */ - public List getTagList() { - List list = new ArrayList<>(); - for (int i = 0; i < mItems.size(); i++) { - list.add(mItems.get(i).item); - } - return list; - } - - /** - * 添加水波纹 - */ - public TagViewGroup addRipple() { - mRippleView = new RippleView(getContext()); - mRippleView.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)); - mRippleView.setDirection(DIRECTION.CENTER); - mRippleView.initAnimator(mRippleMinRadius, mRippleMaxRadius, mRippleAlpha); - addView(mRippleView); - return this; - } - - public TagViewGroup setShowAnimator(Animator animator) { - mShowAnimator = animator; - return this; - } - - public TagViewGroup setHideAnimator(Animator animator) { - mHideAnimator = animator; - return this; - } - - public void startShowAnimator() { - if (mShowAnimator != null && !mShowAnimator.isRunning()) { - mShowAnimator.start(); - } - } - - public void startHideAnimator() { - if (mHideAnimator != null && !mHideAnimator.isRunning()) { - mHideAnimator.start(); - } - } - - public void setTagAdapter(TagAdapter adapter) { - if (mAdapter != null) { - mAdapter.unregisterDataSetObserver(mObserver); - clearGroup(); - } - mAdapter = adapter; - if (mAdapter != null) { - mAdapter.registerDataSetObserver(mObserver); - } - populate(); - } - - public TagAdapter getTagAdapter() { - return mAdapter; - } - - void clearGroup() { - for (int i = 0; i < mItems.size(); i++) { - ItemInfo itemInfo = mItems.get(i); - mAdapter.destroyItem(this, itemInfo.position, itemInfo.item); - mItems.clear(); - removeAllViews(); - } - } - - void populate() { - int count = mAdapter.getCount(); - if (count < 0 || count > DEFAULT_MAX_TAG) { - throw new IllegalStateException("TagView count must >= 0 and <= " + DEFAULT_MAX_TAG); - } - for (int i = 0; i < count; i++) { - addnewItem(i); - } - } - - void dataSetChanged() { - clearGroup(); - populate(); - } - - ItemInfo infoForChild(View child) { - for (int i = 0; i < mItems.size(); i++) { - ItemInfo info = mItems.get(i); - if (mAdapter.isViewFromObject(child, info)) { - return info; - } - } - return null; - } - - ItemInfo infoForTagView(ITagView tagView) { - for (int i = 0; i < mItems.size(); i++) { - ItemInfo info = mItems.get(i); - if (info.item.equals(tagView)) { - return info; - } - } - return null; - } - - private void refreshTagsInfo(ITagView child) { - if (child.getDirection() != DIRECTION.CENTER) { - infoForTagView(child).rectF.set(child.getLeft(), child.getTop(), child.getRight(), child.getBottom()); - } - } - - /** - * 检测 Touch 事件发生在哪个 Tag 上 - * - * @param x - * @param y - * @return - */ - private ItemInfo isTouchingTags(float x, float y) { - for (int i = 0; i < mItems.size(); i++) { - ItemInfo info = mItems.get(i); - if (info.rectF.contains(x, y)) { - return info; - } - } - return null; - } - - //设置中心圆点坐标占整个 ViewGroup 的比例 - - public void setPercent(float percentX, float percentY) { - mPercentX = percentX; - mPercentY = percentY; - } - - @Override - public boolean onTouchEvent(MotionEvent event) { - if (mClickListener != null) { - return mGestureDetector.onTouchEvent(event); - } - return super.onTouchEvent(event); - } - - public void setOnTagGroupClickListener(OnTagGroupClickListener listener) { - mClickListener = listener; - } - - public void setOnTagGroupDragListener(OnTagGroupDragListener listener) { - this.mScrollListener = listener; - } - - public interface OnTagGroupClickListener { - - //TagGroup 中心圆点被点击 - void onCircleClick(TagViewGroup container); - - //TagGroup Tag子view被点击 - void onTagClick(TagViewGroup container, ITagView tag, int position); - - //TagGroup 被长按 - void onLongPress(TagViewGroup container); - } - - public interface OnTagGroupDragListener { - //TagGroup 拖动 - void onDrag(TagViewGroup container, float percentX, float percentY); - } - - //内部处理 touch 事件监听器 - private class TagOnGestureListener extends GestureDetector.SimpleOnGestureListener { - - @Override - public boolean onDown(MotionEvent e) { - float x = e.getX(); - float y = e.getY(); - if (mCenterRect.contains(x, y) || isTouchingTags(x, y) != null) { - return true; - } - return super.onDown(e); - } - - @Override - public boolean onSingleTapUp(MotionEvent e) { - float x = e.getX(); - float y = e.getY(); - - if (mCenterRect.contains(x, y)) { - mClickListener.onCircleClick(TagViewGroup.this); - } else { - ItemInfo info = isTouchingTags(x, y); - if (info != null) { - mClickListener.onTagClick(TagViewGroup.this, info.item, info.position); - } - } - return true; - } - - @Override - public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { - if (mScrollListener != null) { - float currentX = mCenterX - distanceX; - float currentY = mCenterY - distanceY; - currentX = Math.min(Math.max(currentX, mChildUsed[0]), getMeasuredWidth() - mChildUsed[2]); - currentY = Math.min(Math.max(currentY, mChildUsed[1]), getMeasuredHeight() - mChildUsed[3]); - mPercentX = currentX / getMeasuredWidth(); - mPercentY = currentY / getMeasuredHeight(); - invalidate(); - requestLayout(); - mScrollListener.onDrag(TagViewGroup.this, mPercentX, mPercentY); - } - return true; - } - - @Override - public void onLongPress(MotionEvent e) { - super.onLongPress(e); - float x = e.getX(); - float y = e.getY(); - if (mCenterRect.contains(x, y) || isTouchingTags(x, y) != null) { - mClickListener.onLongPress(TagViewGroup.this); - } - } - } - - /** - * 设置中心圆的半径 - */ - @SuppressWarnings("unused") - public void setCircleRadius(int radius) { - mRadius = radius; - invalidate(); - } - - public int getCircleRadius() { - return mRadius; - } - - /** - * 设置中心内圆半径 - */ - @SuppressWarnings("unused") - public void setCircleInnerRadius(int innerRadius) { - mInnerRadius = innerRadius; - invalidate(); - } - - public int getCircleInnerRadius() { - return mInnerRadius; - } - - /** - * 设置线条显示比例 - */ - @SuppressWarnings("unused") - public void setLinesRatio(float ratio) { - mLinesRatio = ratio; - invalidate(); - } - - public float getLinesRatio() { - return mLinesRatio; - } - - /** - * 设置 Tag 的透明度 - */ - @SuppressWarnings("unused") - public void setTagAlpha(float alpha) { - mTagAlpha = alpha; - drawTagAlpha(mTagAlpha); - } - - public float getTagAlpha() { - return mTagAlpha; - } - - /** - * 设置线条宽度 - * - * @param lineWidth 线条宽度 - */ - public void setLineWidth(int lineWidth) { - mLinesWidth = lineWidth; - invalidate(); - } - - /** - * 得到线条宽度 - * - * @return - */ - public int getLineWidth() { - return mLinesWidth; - } - - /** - * 设置圆心到折点的垂直距离 - * - * @param vDistance 垂直距离 - */ - public void setVDistance(int vDistance) { - mVDistance = vDistance; - } - - public int getVDistance() { - return mVDistance; - } - - /** - * 设置圆心到斜线折点的垂直距离 - * - * @param titlDistance 垂直距离 - */ - public void setTitlDistance(int titlDistance) { - mTDistance = titlDistance; - } - - public int getTitlDistance() { - return mTDistance; - } - - /** - * 设置水波纹最大半径 - * - * @param radius 最大半径 - */ - public void setRippleMaxRadius(int radius) { - mRippleMaxRadius = radius; - } - - public int getRippleMaxRadius() { - return mRippleMaxRadius; - } - - /** - * 设置水波纹起始透明度 - * - * @param alpha 透明度 - */ - public void setRippleAlpha(int alpha) { - mRippleAlpha = alpha; - } - - public int getRippleAlpha() { - return mRippleAlpha; - } - - public class TagSetObserver extends DataSetObserver { - - @Override - public void onChanged() { - dataSetChanged(); - } - } - - public static final Property CIRCLE_RADIUS = new Property(Integer.class, "circleRadius") { - @Override - public Integer get(TagViewGroup object) { - return object.getCircleRadius(); - } - - @Override - public void set(TagViewGroup object, Integer value) { - object.setCircleRadius(value); - } - }; - - public static final Property CIRCLE_INNER_RADIUS = new Property(Integer.class, "circleInnerRadius") { - @Override - public Integer get(TagViewGroup object) { - return object.getCircleInnerRadius(); - } - - @Override - public void set(TagViewGroup object, Integer value) { - object.setCircleInnerRadius(value); - } - }; - - public static final Property LINES_RATIO = new Property(Float.class, "linesRatio") { - @Override - public Float get(TagViewGroup object) { - return object.getLinesRatio(); - } - - @Override - public void set(TagViewGroup object, Float value) { - object.setLinesRatio(value); - } - }; - - public static final Property TAG_ALPHA = new Property(Float.class, "tagAlpha") { - @Override - public Float get(TagViewGroup object) { - return object.getTagAlpha(); - } - - @Override - public void set(TagViewGroup object, Float value) { - object.setTagAlpha(value); - } - }; -} diff --git a/library/src/main/java/com/licrafter/tagview/utils/DipConvertUtils.java b/library/src/main/java/com/licrafter/tagview/utils/DipConvertUtils.java deleted file mode 100644 index 124984f..0000000 --- a/library/src/main/java/com/licrafter/tagview/utils/DipConvertUtils.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.licrafter.tagview.utils; - -import android.content.Context; - -/** - * author: shell - * date 2016/12/22 下午1:04 - **/ -public class DipConvertUtils { - - public static int dip2px(Context context, float dpValue) { - final float scale = context.getResources().getDisplayMetrics().density; - return (int) (dpValue * scale + 0.5f); - } -} diff --git a/library/src/main/java/com/licrafter/tagview/views/ITagView.java b/library/src/main/java/com/licrafter/tagview/views/ITagView.java deleted file mode 100644 index be02200..0000000 --- a/library/src/main/java/com/licrafter/tagview/views/ITagView.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.licrafter.tagview.views; - -import com.licrafter.tagview.DIRECTION; - -/** - * author: shell - * date 2016/12/22 下午1:04 - **/ - -public interface ITagView { - - // set tag direction - void setDirection(DIRECTION direction); - - // get tag dirction - DIRECTION getDirection(); - - int getMeasuredWidth(); - - int getMeasuredHeight(); - - int getTop(); - - int getLeft(); - - int getRight(); - - int getBottom(); - - void layout(int left, int top, int right, int bottom); - -} diff --git a/library/src/main/java/com/licrafter/tagview/views/RippleView.java b/library/src/main/java/com/licrafter/tagview/views/RippleView.java deleted file mode 100644 index 2fbf060..0000000 --- a/library/src/main/java/com/licrafter/tagview/views/RippleView.java +++ /dev/null @@ -1,110 +0,0 @@ -package com.licrafter.tagview.views; - -import android.animation.AnimatorSet; -import android.animation.ObjectAnimator; -import android.animation.ValueAnimator; -import android.content.Context; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -import android.util.AttributeSet; -import android.view.View; -import android.view.animation.AccelerateInterpolator; - -import com.licrafter.tagview.DIRECTION; -import com.licrafter.tagview.utils.DipConvertUtils; - -/** - * author: shell - * date 2016/12/25 下午7:18 - **/ -public class RippleView extends View implements ITagView { - - private int mRadius; - private int mAlpha; - private DIRECTION mDirection; - private Paint mPaint; - private AnimatorSet mAnimator; - private int mX, mY; - - public RippleView(Context context) { - this(context, null); - } - - public RippleView(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public RippleView(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - mPaint = new Paint(); - mPaint.setColor(Color.WHITE); - } - - @Override - protected void onDraw(Canvas canvas) { - super.onDraw(canvas); - mPaint.setAlpha(mAlpha); - canvas.drawCircle(mX, mY, mRadius, mPaint); - } - - @Override - protected void onDetachedFromWindow() { - super.onDetachedFromWindow(); - stopRipple(); - } - - @Override - protected void onAttachedToWindow() { - super.onAttachedToWindow(); - startRipple(); - } - - @Override - public void setDirection(DIRECTION direction) { - mDirection = direction; - } - - @Override - public DIRECTION getDirection() { - return mDirection; - } - - public void startRipple() { - mAnimator.start(); - } - - public void stopRipple() { - mAnimator.end(); - } - - //设置水波纹半径 - public void setRippleRadius(int radius) { - mRadius = radius; - invalidate(); - } - - //设置水波纹 alpha 范围[0-255] - public void setRippleAlpha(int alpha) { - mAlpha = alpha; - invalidate(); - } - - public void setCenterPoint(int x, int y) { - mX = x; - mY = y; - } - - public void initAnimator(int minRadius, int maxRadius, int alpha) { - ObjectAnimator radiusAnimator = ObjectAnimator.ofInt(this, "RippleRadius", minRadius, maxRadius); - radiusAnimator.setRepeatMode(ValueAnimator.RESTART); - radiusAnimator.setRepeatCount(ValueAnimator.INFINITE); - ObjectAnimator alphaAnimator = ObjectAnimator.ofInt(this, "RippleAlpha", alpha, 0); - alphaAnimator.setRepeatMode(ValueAnimator.RESTART); - alphaAnimator.setRepeatCount(ValueAnimator.INFINITE); - mAnimator = new AnimatorSet(); - mAnimator.playTogether(radiusAnimator, alphaAnimator); - mAnimator.setDuration(1000); - mAnimator.setInterpolator(new AccelerateInterpolator()); - } -} diff --git a/library/src/main/java/com/licrafter/tagview/views/TagTextView.java b/library/src/main/java/com/licrafter/tagview/views/TagTextView.java deleted file mode 100644 index 7ecf469..0000000 --- a/library/src/main/java/com/licrafter/tagview/views/TagTextView.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.licrafter.tagview.views; - -import android.content.Context; -import android.graphics.Color; -import android.util.AttributeSet; -import android.widget.TextView; - -import com.licrafter.tagview.DIRECTION; -import com.licrafter.tagview.utils.DipConvertUtils; - -/** - * author: shell - * date 2016/12/20 下午3:43 - **/ -public class TagTextView extends TextView implements ITagView { - - private DIRECTION mDirection; - - public TagTextView(Context context) { - this(context, null); - } - - public TagTextView(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public TagTextView(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - setTextColor(Color.WHITE); - setTextSize(13); - setShadowLayer(7, 0, 0, Color.BLACK); - setPadding(DipConvertUtils.dip2px(getContext(), 12), DipConvertUtils.dip2px(getContext(), 4) - , DipConvertUtils.dip2px(getContext(), 12), DipConvertUtils.dip2px(getContext(), 4)); - } - - @Override - public void setDirection(DIRECTION direction) { - mDirection = direction; - } - - @Override - public DIRECTION getDirection() { - return mDirection; - } -} diff --git a/library/src/main/kotlin/com/licrafter/tagview/DIRECTION.kt b/library/src/main/kotlin/com/licrafter/tagview/DIRECTION.kt new file mode 100644 index 0000000..aaaaef1 --- /dev/null +++ b/library/src/main/kotlin/com/licrafter/tagview/DIRECTION.kt @@ -0,0 +1,19 @@ +package com.licrafter.tagview + +/** + * Created by lijx on 2018/5/7. + * Gmail: shellljx@gmail.com + */ +enum class DIRECTION { + LEFT_CENTER, + LEFT_TOP, + LEFT_TOP_STRAIGHT, + LEFT_BOTTOM, + LEFT_BOTTOM_STRAIGHT, + RIGHT_CENTER, + RIGHT_TOP, + RIGHT_TOP_STRAIGHT, + RIGHT_BOTTOM, + RIGHT_BOTTOM_STRAIGHT, + CENTER; +} \ No newline at end of file diff --git a/library/src/main/kotlin/com/licrafter/tagview/TagAdapter.kt b/library/src/main/kotlin/com/licrafter/tagview/TagAdapter.kt new file mode 100644 index 0000000..fb5ee49 --- /dev/null +++ b/library/src/main/kotlin/com/licrafter/tagview/TagAdapter.kt @@ -0,0 +1,61 @@ +package com.licrafter.tagview + +import android.database.DataSetObservable +import android.database.DataSetObserver +import android.util.Log +import android.view.View +import android.view.ViewGroup +import com.licrafter.tagview.views.ITagView + +/** + * Created by lijx on 2018/5/7. + * Gmail: shellljx@gmail.com + */ +abstract class TagAdapter { + + private var mObservable = DataSetObservable() + + /** + * Return the number of ITagView. + */ + abstract fun getCount(): Int + + abstract fun getItem(position: Int): ITagView + + fun instantiateItem(container: ViewGroup, position: Int): ITagView { + val itemId = getItemId(position).toLong() + val tagView = getItem(position) + if (DEBUG) Log.v(TAG, "Adding tagView #$itemId: TagView=$tagView") + container.addView(tagView as View) + return tagView + } + + fun isViewFromObject(view: View, obj: Any): Boolean { + return obj == view + } + + fun destroyItem(container: ViewGroup, position: Int, tagView: ITagView) { + + } + + fun getItemId(position: Int): Int { + return position + } + + fun notifyDataSetChanged() { + mObservable.notifyChanged() + } + + fun registerDataSetObserver(observer: DataSetObserver) { + mObservable.registerObserver(observer) + } + + fun unregisterDataSetObserver(observer: DataSetObserver) { + mObservable.unregisterObserver(observer) + } + + companion object { + const val DEBUG = false + const val TAG = "TagAdapter" + } +} \ No newline at end of file diff --git a/library/src/main/kotlin/com/licrafter/tagview/TagViewGroup.kt b/library/src/main/kotlin/com/licrafter/tagview/TagViewGroup.kt new file mode 100644 index 0000000..ae66cae --- /dev/null +++ b/library/src/main/kotlin/com/licrafter/tagview/TagViewGroup.kt @@ -0,0 +1,738 @@ +package com.licrafter.tagview + +import android.animation.Animator +import android.content.Context +import android.database.DataSetObserver +import android.graphics.* +import android.support.v4.view.GestureDetectorCompat +import android.util.AttributeSet +import android.util.Property +import android.view.GestureDetector +import android.view.MotionEvent +import android.view.View +import android.view.ViewGroup +import com.licrafter.tagview.utils.DipConvertUtils +import com.licrafter.tagview.views.ITagView +import com.licrafter.tagview.views.RippleView +import java.util.ArrayList + +/** + * Created by lijx on 2018/4/22. + * Gmail: shellljx@gmail.com + */ +class TagViewGroup : ViewGroup { + + private var mPaint = Paint() + private var mPath = Path() + private var mDstPath = Path() + private val mPathMeasure = PathMeasure() + private var mShowAnimator: Animator? = null + private var mHideAnimator: Animator? = null + private var mAdapter: TagAdapter? = null + private var mGestureDetector: GestureDetectorCompat + private var mClickListener: OnTagGroupClickListener? = null + private var mScrollListener: OnTagGroupDragListener? = null + private val mObserver = TagSetObserver() + private var mRippleView: RippleView? = null + + + private var mRippleMaxRadius: Int //水波纹最大半径 + private val mRippleMinRadius: Int //水波纹最小半径 + private var mRippleAlpha: Int //水波纹起始透明度 + private var mRadius: Int //外圆半径 + private var mInnerRadius: Int //内圆半径 + private var mTDistance: Int //斜线长度 + private var mVDistance: Int //竖直(上/下)方向线条长度 + private var mTagAlpha: Float = 0f //Tag标签的透明度 + private val mCenterRect = RectF() + private val mItems = ArrayList() + private var mChildUsed: IntArray = IntArray(4) + private var mCenterX: Int = 0 //圆心 X 坐标 + private var mCenterY: Int = 0 //圆心 Y 坐标 + private var mPercentX: Float = 0f + private var mPercentY: Float = 0f + private var mLinesWidth: Int = 0 //线条宽度 + + private var mLinesRatio = 1f + + + class ItemInfo { + var item: ITagView? = null + var position: Int = 0 + var rectF = RectF() + } + + constructor(context: Context) : this(context, null) + constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0) + constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { + val theme = context.theme + val array = theme.obtainStyledAttributes(attrs, R.styleable.TagViewGroup, defStyleAttr, 0) + try { + mRadius = array.getDimensionPixelSize(R.styleable.TagViewGroup_radius, DipConvertUtils.dip2px(context, DEFAULT_RADIUS.toFloat())) + mInnerRadius = array.getDimensionPixelSize(R.styleable.TagViewGroup_inner_radius, DipConvertUtils.dip2px(context, DEFAULT_INNER_RADIUS.toFloat())) + mTDistance = array.getDimensionPixelSize(R.styleable.TagViewGroup_tilt_distance, DipConvertUtils.dip2px(context, DEFAULT_TILT_DISTANCE.toFloat())) + mVDistance = array.getDimensionPixelSize(R.styleable.TagViewGroup_v_distance, DipConvertUtils.dip2px(context, DEFAULT_V_DISTANCE.toFloat())) + mLinesWidth = array.getDimensionPixelSize(R.styleable.TagViewGroup_line_width, DipConvertUtils.dip2px(context, DEFAULT_LINES_WIDTH.toFloat())) + mRippleMaxRadius = array.getDimensionPixelSize(R.styleable.TagViewGroup_ripple_maxRadius, DipConvertUtils.dip2px(context, DEFAULT_RIPPLE_MAX_RADIUS.toFloat())) + mRippleAlpha = array.getInteger(R.styleable.TagViewGroup_ripple_alpha, DEFULT_RIPPLE_ALPHA) + mRippleMinRadius = mInnerRadius + (mRadius - mInnerRadius) / 2 + } finally { + array.recycle() + } + + mPaint.isAntiAlias = true + mGestureDetector = GestureDetectorCompat(context, TagOnGestureListener()) + } + + override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec) + measureChildren(widthMeasureSpec, heightMeasureSpec) + mChildUsed = getChildUsed() + //园中心默认在左上角 (0,0) + mCenterX = (measuredWidth * mPercentX).toInt() + mCenterY = (measuredHeight * mPercentY).toInt() + checkBounds() + mCenterRect.set((mCenterX - mRadius).toFloat(), (mCenterY - mRadius).toFloat(), (mCenterX + mRadius).toFloat(), (mCenterY + mRadius).toFloat()) + mRippleView?.let { + it.setCenterPoint(mCenterX, mCenterY) + } + } + + /** + * 获取中心圆上下左右各个方向的宽度 + * + * @return int[]{left,top,right,bottom} + */ + private fun getChildUsed(): IntArray { + var leftMax = mVDistance + var topMax = mVDistance + var rightMax = mVDistance + var bottomMax = mVDistance + + for (i in 0 until childCount) { + //todo 转换有可能出错 + val child = getChildAt(i) as ITagView + when (child.getDirection()) { + DIRECTION.RIGHT_TOP_STRAIGHT//右上斜直线 + -> { + rightMax = Math.max(rightMax, mTDistance + child.getMeasuredWidth()) + topMax = Math.max(topMax, child.getMeasuredHeight() + mTDistance) + } + DIRECTION.RIGHT_TOP//右上折线 + -> { + rightMax = Math.max(rightMax, child.getMeasuredWidth()) + topMax = Math.max(topMax, child.getMeasuredHeight() + mVDistance) + } + DIRECTION.RIGHT_CENTER//右中折线 + -> { + rightMax = Math.max(rightMax, child.getMeasuredWidth()) + topMax = Math.max(topMax, Math.max(mVDistance, child.getMeasuredHeight())) + } + DIRECTION.RIGHT_BOTTOM//右下折线 + -> { + rightMax = Math.max(rightMax, child.getMeasuredWidth()) + bottomMax = mVDistance + } + DIRECTION.RIGHT_BOTTOM_STRAIGHT -> { + rightMax = Math.max(rightMax, mTDistance + child.getMeasuredWidth()) + bottomMax = mTDistance + } + DIRECTION.LEFT_TOP//左上折线 + -> { + leftMax = Math.max(leftMax, child.getMeasuredWidth()) + topMax = Math.max(topMax, child.getMeasuredHeight() + mVDistance) + } + DIRECTION.LEFT_TOP_STRAIGHT//左上斜直线 + -> { + leftMax = Math.max(leftMax, child.getMeasuredWidth() + mTDistance) + topMax = Math.max(topMax, child.getMeasuredHeight() + mTDistance) + } + DIRECTION.LEFT_CENTER//左中折线 + -> { + leftMax = Math.max(leftMax, child.getMeasuredWidth()) + topMax = Math.max(topMax, Math.max(mVDistance, child.getMeasuredHeight())) + } + DIRECTION.LEFT_BOTTOM//左下折线 + -> { + leftMax = Math.max(leftMax, child.getMeasuredWidth()) + bottomMax = mVDistance + } + DIRECTION.LEFT_BOTTOM_STRAIGHT//左下斜直线 + -> { + leftMax = Math.max(leftMax, child.getMeasuredWidth() + mTDistance) + bottomMax = mTDistance + } + DIRECTION.CENTER + -> { + } + } + + } + return intArrayOf(leftMax, topMax, rightMax, bottomMax) + } + + private fun checkBounds() { + val rightAvailable = measuredWidth - mCenterX + val leftAvailable = mCenterX + if (mChildUsed[2] > rightAvailable) { + mCenterX -= mChildUsed[2] - rightAvailable + } + if (mChildUsed[0] > leftAvailable) { + mCenterX += mChildUsed[0] - leftAvailable + } + } + + override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) { + var left = 0 + var top = 0 + for (i in 0 until childCount) { + val child = getChildAt(i) as ITagView + when (child.getDirection()) { + DIRECTION.RIGHT_TOP_STRAIGHT//右上斜线 + -> { + top = mCenterY - mTDistance - child.getMeasuredHeight() + left = mCenterX + mTDistance + } + DIRECTION.RIGHT_TOP//右上 + -> { + left = mCenterX + top = mCenterY - mVDistance - child.getMeasuredHeight() + } + DIRECTION.RIGHT_CENTER//右中 + -> { + left = mCenterX + top = mCenterY - child.getMeasuredHeight() + } + DIRECTION.RIGHT_BOTTOM//右下 + -> { + left = mCenterX + top = mVDistance + mCenterY - child.getMeasuredHeight() + } + DIRECTION.RIGHT_BOTTOM_STRAIGHT//右下斜线 + -> { + left = mCenterX + mTDistance + top = mTDistance + mCenterY - child.getMeasuredHeight() + } + DIRECTION.LEFT_TOP//左上 + -> { + left = mCenterX - child.getMeasuredWidth() + top = mCenterY - mVDistance - child.getMeasuredHeight() + } + DIRECTION.LEFT_TOP_STRAIGHT//左上斜线 + -> { + left = mCenterX - child.getMeasuredWidth() - mTDistance + top = mCenterY - mTDistance - child.getMeasuredHeight() + } + DIRECTION.LEFT_CENTER//左中 + -> { + left = mCenterX - child.getMeasuredWidth() + top = mCenterY - child.getMeasuredHeight() + } + DIRECTION.LEFT_BOTTOM//左下 + -> { + left = mCenterX - child.getMeasuredWidth() + top = mVDistance + mCenterY - child.getMeasuredHeight() + } + DIRECTION.LEFT_BOTTOM_STRAIGHT//左下斜线 + -> { + left = mCenterX - child.getMeasuredWidth() - mTDistance + top = mTDistance + mCenterY - child.getMeasuredHeight() + } + DIRECTION.CENTER -> { + left = 0 + top = 0 + } + } + child.layout(left, top, left + child.getMeasuredWidth(), top + child.getMeasuredHeight()) + refreshTagsInfo(child) + } + } + + override fun dispatchDraw(canvas: Canvas) { + super.dispatchDraw(canvas) + //绘制折线 + drawLines(canvas) + //绘制外圆 + mPaint.style = Paint.Style.FILL + mPaint.color = Color.parseColor("#30000000") + canvas.drawCircle(mCenterX.toFloat(), mCenterY.toFloat(), mRadius.toFloat(), mPaint) + //绘制内圆 + mPaint.style = Paint.Style.FILL + mPaint.color = Color.WHITE + canvas.drawCircle(mCenterX.toFloat(), mCenterY.toFloat(), mInnerRadius.toFloat(), mPaint) + } + + override fun onAttachedToWindow() { + mRippleView?.let { it.startRipple() } + super.onAttachedToWindow() + } + + override fun onDetachedFromWindow() { + mRippleView?.let { it.stopRipple() } + super.onDetachedFromWindow() + } + + private fun drawTagAlpha(alpha: Float) { + for (i in 0 until childCount) { + getChildAt(i).alpha = alpha + } + } + + private fun drawLines(canvas: Canvas) { + mPaint.color = Color.WHITE + mPaint.strokeWidth = mLinesWidth.toFloat() + mPaint.style = Paint.Style.STROKE + + for (i in 0 until childCount) { + val child = getChildAt(i) as ITagView + mPath.reset() + mPath.moveTo(mCenterX.toFloat(), mCenterY.toFloat()) + mDstPath.reset() + mDstPath.rLineTo(0f, 0f) + when (child.getDirection()) { + DIRECTION.RIGHT_TOP//右上 + , DIRECTION.RIGHT_BOTTOM//右下 + , DIRECTION.RIGHT_TOP_STRAIGHT//右上斜线 + , DIRECTION.RIGHT_BOTTOM_STRAIGHT//右下斜线 + -> { + mPath.lineTo(child.getLeft().toFloat(), child.getBottom().toFloat()) + mPath.lineTo(child.getRight().toFloat(), child.getBottom().toFloat()) + } + DIRECTION.RIGHT_CENTER//右中 + -> mPath.lineTo(child.getRight().toFloat(), child.getBottom().toFloat()) + DIRECTION.LEFT_TOP//左上 + , DIRECTION.LEFT_TOP_STRAIGHT//左上斜线 + , DIRECTION.LEFT_BOTTOM//左下 + , DIRECTION.LEFT_BOTTOM_STRAIGHT//左下斜线 + -> { + mPath.lineTo(child.getRight().toFloat(), child.getBottom().toFloat()) + mPath.lineTo(child.getLeft().toFloat(), child.getBottom().toFloat()) + } + DIRECTION.LEFT_CENTER//左中 + -> mPath.lineTo(child.getLeft().toFloat(), child.getBottom().toFloat()) + } + mPathMeasure.setPath(mPath, false) + mPathMeasure.getSegment(0f, mPathMeasure.length * mLinesRatio, mDstPath, true) + canvas.drawPath(mDstPath, mPaint) + } + } + + private fun addnewItem(position: Int): ItemInfo { + val itemInfo = ItemInfo() + itemInfo.position = position + itemInfo.item = mAdapter?.instantiateItem(this, position) + mItems.add(itemInfo) + return itemInfo + } + + /** + * 得到 TagViewGroup 中的所有标签列表 + * + * @return + */ + fun getTagList(): List { + val list = ArrayList() + for (i in mItems.indices) { + list.add(mItems[i].item!!) + } + return list + } + + /** + * 添加水波纹 + */ + fun addRipple(): TagViewGroup { + mRippleView = RippleView(context) + mRippleView!!.layoutParams = ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT) + mRippleView!!.setDirection(DIRECTION.CENTER) + mRippleView!!.initAnimator(mRippleMinRadius, mRippleMaxRadius, mRippleAlpha) + addView(mRippleView) + return this + } + + fun startShowAnimator() { + mShowAnimator?.let { + if (!it.isRunning) { + it.start() + } + } + } + + fun startHideAnimator() { + mHideAnimator?.let { + if (!it.isRunning) { + it.start() + } + } + } + + fun setShowAnimator(animator: Animator): TagViewGroup { + mShowAnimator = animator + return this + } + + fun setHideAnimator(animator: Animator): TagViewGroup { + mHideAnimator = animator + return this + } + + fun setTagAdapter(adapter: TagAdapter) { + mAdapter?.let { + it.unregisterDataSetObserver(mObserver) + clearGroup() + } + mAdapter = adapter + mAdapter?.registerDataSetObserver(mObserver) + populate() + } + + fun getTagAdapter(): TagAdapter? { + return mAdapter + } + + private fun clearGroup() { + for (i in mItems.indices) { + val itemInfo = mItems[i] + mAdapter?.destroyItem(this, itemInfo.position, itemInfo.item!!) + } + mItems.clear() + removeAllViews() + } + + private fun populate() { + var count = mAdapter?.getCount() ?: 0 + if (count < 0 || count > DEFAULT_MAX_TAG) { + throw IllegalStateException("TagView count must >= 0 and <= $DEFAULT_MAX_TAG") + } + for (i in 0 until count) { + addnewItem(i) + } + } + + + internal fun dataSetChanged() { + clearGroup() + populate() + } + + internal fun infoForChild(child: View): ItemInfo? { + for (i in mItems.indices) { + val info = mItems[i] + mAdapter?.let { + if (it.isViewFromObject(child, info)) return info + } + } + return null + } + + private fun refreshTagsInfo(child: ITagView) { + //todo CENTER被过滤掉?最好不要特异性 + if (child.getDirection() != DIRECTION.CENTER) { + infoForTagView(child)!!.rectF.set(child.getLeft().toFloat(), child.getTop().toFloat(), child.getRight().toFloat(), child.getBottom().toFloat()) + } + } + + fun infoForTagView(tagView: ITagView): ItemInfo? { + for (i in mItems.indices) { + val info = mItems[i] + if (info.item == tagView) { + return info + } + } + return null + } + + + /** + * 检测 Touch 事件发生在哪个 Tag 上 + * + * @param x + * @param y + * @return + */ + private fun isTouchingTags(x: Float, y: Float): ItemInfo? { + for (i in mItems.indices) { + val info = mItems[i] + if (info.rectF.contains(x, y)) { + return info + } + } + return null + } + + //设置中心圆点坐标占整个 ViewGroup 的比例 + + fun setPercent(percentX: Float, percentY: Float) { + mPercentX = percentX + mPercentY = percentY + } + + override fun onTouchEvent(event: MotionEvent?): Boolean { + return mClickListener?.let { + return mGestureDetector.onTouchEvent(event) + } ?: super.onTouchEvent(event) + } + + fun setOnTagGroupClickListener(listener: OnTagGroupClickListener) { + mClickListener = listener + } + + fun setOnTagGroupDragListener(listener: OnTagGroupDragListener) { + this.mScrollListener = listener + } + + interface OnTagGroupClickListener { + + //TagGroup 中心圆点被点击 + fun onCircleClick(container: TagViewGroup) + + //TagGroup Tag子view被点击 + fun onTagClick(container: TagViewGroup, tag: ITagView, position: Int) + + //TagGroup 被长按 + fun onLongPress(container: TagViewGroup) + } + + interface OnTagGroupDragListener { + //TagGroup 拖动 + fun onDrag(container: TagViewGroup, percentX: Float, percentY: Float) + } + + //内部处理 touch 事件监听器 + private inner class TagOnGestureListener : GestureDetector.SimpleOnGestureListener() { + override fun onDown(e: MotionEvent): Boolean { + return if (mCenterRect.contains(e.x, e.y) || isTouchingTags(e.x, e.y) != null) { + true + } else super.onDown(e) + } + + override fun onSingleTapUp(e: MotionEvent): Boolean { + val x = e.x + val y = e.y + + if (mCenterRect.contains(x, y)) { + mClickListener?.onCircleClick(this@TagViewGroup) + } else { + val info = isTouchingTags(x, y) + info?.let { + mClickListener?.onTagClick(this@TagViewGroup, it.item!!, info.position) + } + } + return true + } + + override fun onScroll(e1: MotionEvent, e2: MotionEvent, distanceX: Float, distanceY: Float): Boolean { + mScrollListener?.let { + var currentX = mCenterX - distanceX + var currentY = mCenterY - distanceY + currentX = Math.min(Math.max(currentX, mChildUsed[0].toFloat()), (measuredWidth - mChildUsed[2]).toFloat()) + currentY = Math.min(Math.max(currentY, mChildUsed[1].toFloat()), (measuredHeight - mChildUsed[3]).toFloat()) + mPercentX = currentX / measuredWidth + mPercentY = currentY / measuredHeight + invalidate() + requestLayout() + it.onDrag(this@TagViewGroup, mPercentX, mPercentY) + } + return true + } + + override fun onLongPress(e: MotionEvent) { + super.onLongPress(e) + val x = e.x + val y = e.y + if (x.equals(y)) + if (mCenterRect.contains(x, y) || isTouchingTags(x, y) != null) { + mClickListener?.onLongPress(this@TagViewGroup) + } + } + } + + /** + * 设置中心圆的半径 + */ + fun setCircleRadius(radius: Int) { + mRadius = radius + invalidate() + } + + fun getCircleRadius(): Int { + return mRadius + } + + /** + * 设置中心内圆半径 + */ + fun setCircleInnerRadius(innerRadius: Int) { + mInnerRadius = innerRadius + invalidate() + } + + fun getCircleInnerRadius(): Int { + return mInnerRadius + } + + /** + * 设置线条显示比例 + */ + fun setLinesRatio(ratio: Float) { + mLinesRatio = ratio + invalidate() + } + + fun getLinesRatio(): Float { + return mLinesRatio + } + + /** + * 设置 Tag 的透明度 + */ + fun setTagAlpha(alpha: Float) { + mTagAlpha = alpha + drawTagAlpha(mTagAlpha) + } + + fun getTagAlpha(): Float { + return mTagAlpha + } + + /** + * 设置线条宽度 + * + * @param lineWidth 线条宽度 + */ + fun setLineWidth(lineWidth: Int) { + mLinesWidth = lineWidth + invalidate() + } + + /** + * 得到线条宽度 + * + * @return + */ + fun getLineWidth(): Int { + return mLinesWidth + } + + /** + * 设置圆心到折点的垂直距离 + * + * @param vDistance 垂直距离 + */ + fun setVDistance(vDistance: Int) { + mVDistance = vDistance + } + + fun getVDistance(): Int { + return mVDistance + } + + /** + * 设置圆心到斜线折点的垂直距离 + * + * @param titlDistance 垂直距离 + */ + fun setTitlDistance(titlDistance: Int) { + mTDistance = titlDistance + } + + fun getTitlDistance(): Int { + return mTDistance + } + + /** + * 设置水波纹最大半径 + * + * @param radius 最大半径 + */ + fun setRippleMaxRadius(radius: Int) { + mRippleMaxRadius = radius + } + + fun getRippleMaxRadius(): Int { + return mRippleMaxRadius + } + + /** + * 设置水波纹起始透明度 + * + * @param alpha 透明度 + */ + fun setRippleAlpha(alpha: Int) { + mRippleAlpha = alpha + } + + fun getRippleAlpha(): Int { + return mRippleAlpha + } + + inner class TagSetObserver : DataSetObserver() { + + override fun onChanged() { + dataSetChanged() + } + } + + companion object { + //默认外圆半径 + const val DEFAULT_RADIUS: Int = 8 + //默认内圆半径 + const val DEFAULT_INNER_RADIUS: Int = 4 + //默认竖直(上/下)方向线条长度 + const val DEFAULT_V_DISTANCE: Int = 28 + //默认斜线长度 + const val DEFAULT_TILT_DISTANCE: Int = 20 + //默认线宽 + const val DEFAULT_LINES_WIDTH: Int = 1 + //默认标签最大数量 + const val DEFAULT_MAX_TAG: Int = 6 + //水波纹默认最大半径 + const val DEFAULT_RIPPLE_MAX_RADIUS: Int = 20 + //默认水波纹透明度 + const val DEFULT_RIPPLE_ALPHA: Int = 100 + + @JvmField + val CIRCLE_RADIUS: Property = object : Property(Int::class.java, "circleRadius") { + override fun get(obj: TagViewGroup): Int { + return obj.getCircleRadius() + } + + override fun set(obj: TagViewGroup, value: Int) { + obj.setCircleRadius(value) + } + } + + @JvmField + val CIRCLE_INNER_RADIUS: Property = object : Property(Int::class.java, "circleInnerRadius") { + override fun get(obj: TagViewGroup): Int { + return obj.getCircleInnerRadius() + } + + override fun set(obj: TagViewGroup, value: Int) { + obj.setCircleInnerRadius(value) + } + } + + @JvmField + val LINES_RATIO: Property = object : Property(Float::class.java, "linesRatio") { + override fun get(obj: TagViewGroup): Float { + return obj.getLinesRatio() + } + + override fun set(obj: TagViewGroup, value: Float) { + obj.setLinesRatio(value) + } + } + + @JvmField + val TAG_ALPHA: Property = object : Property(Float::class.java, "tagAlpha") { + override fun get(obj: TagViewGroup): Float { + return obj.getTagAlpha() + } + + override fun set(obj: TagViewGroup, value: Float) { + obj.setTagAlpha(value) + } + } + } +} \ No newline at end of file diff --git a/library/src/main/kotlin/com/licrafter/tagview/utils/DipConvertUtils.kt b/library/src/main/kotlin/com/licrafter/tagview/utils/DipConvertUtils.kt new file mode 100644 index 0000000..acf289d --- /dev/null +++ b/library/src/main/kotlin/com/licrafter/tagview/utils/DipConvertUtils.kt @@ -0,0 +1,16 @@ +package com.licrafter.tagview.utils + +import android.content.Context + +/** + * Created by lijx on 2018/5/12. + * Gmail: shellljx@gmail.com + */ +object DipConvertUtils { + + @JvmStatic + fun dip2px(context: Context, dpValue: Float): Int { + val scale = context.resources.displayMetrics.density + return (dpValue * scale + 0.5f).toInt() + } +} \ No newline at end of file diff --git a/library/src/main/kotlin/com/licrafter/tagview/views/ITagView.kt b/library/src/main/kotlin/com/licrafter/tagview/views/ITagView.kt new file mode 100644 index 0000000..918193d --- /dev/null +++ b/library/src/main/kotlin/com/licrafter/tagview/views/ITagView.kt @@ -0,0 +1,31 @@ +package com.licrafter.tagview.views + +import com.licrafter.tagview.DIRECTION + +/** + * Created by lijx on 2018/5/7. + * Gmail: shellljx@gmail.com + */ +interface ITagView { + + // set tag direction + fun setDirection(direction: DIRECTION) + + // get tag dirction + fun getDirection(): DIRECTION + + fun getMeasuredWidth(): Int + + fun getMeasuredHeight(): Int + + fun getTop(): Int + + fun getLeft(): Int + + fun getRight(): Int + + fun getBottom(): Int + + fun layout(left: Int, top: Int, right: Int, bottom: Int) + +} \ No newline at end of file diff --git a/library/src/main/kotlin/com/licrafter/tagview/views/RippleView.kt b/library/src/main/kotlin/com/licrafter/tagview/views/RippleView.kt new file mode 100644 index 0000000..8eef721 --- /dev/null +++ b/library/src/main/kotlin/com/licrafter/tagview/views/RippleView.kt @@ -0,0 +1,96 @@ +package com.licrafter.tagview.views + +import android.animation.AnimatorSet +import android.animation.ObjectAnimator +import android.animation.ValueAnimator +import android.content.Context +import android.graphics.Canvas +import android.graphics.Color +import android.graphics.Paint +import android.util.AttributeSet +import android.view.View +import android.view.animation.AccelerateInterpolator +import com.licrafter.tagview.DIRECTION + +/** + * Created by lijx on 2018/5/7. + * Gmail: shellljx@gmail.com + */ +class RippleView : View, ITagView { + + private var mRadius: Float = 0f + private var mAlpha: Int = 0 + private var mDirection: DIRECTION? = null + private val mPaint = Paint() + private lateinit var mAnimator: AnimatorSet + private var mX: Float = 0f + private var mY: Float = 0f + + constructor(context: Context) : this(context, null) + constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0) + constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { + mPaint.color = Color.WHITE + } + + override fun onDraw(canvas: Canvas) { + super.onDraw(canvas) + mPaint.alpha = mAlpha + canvas.drawCircle(mX, mY, mRadius, mPaint) + } + + override fun onDetachedFromWindow() { + super.onDetachedFromWindow() + stopRipple() + } + + override fun onAttachedToWindow() { + super.onAttachedToWindow() + startRipple() + } + + fun startRipple() { + mAnimator.start() + } + + fun stopRipple() { + mAnimator.end() + } + + //设置水波纹半径 + fun setRippleRadius(radius: Int) { + mRadius = radius.toFloat() + invalidate() + } + + //设置水波纹 alpha 范围[0-255] + fun setRippleAlpha(alpha: Int) { + mAlpha = alpha + invalidate() + } + + fun setCenterPoint(x: Int, y: Int) { + mX = x.toFloat() + mY = y.toFloat() + } + + override fun getDirection(): DIRECTION { + return mDirection ?: throw RuntimeException("RippleView has no direction") + } + + override fun setDirection(direction: DIRECTION) { + mDirection = direction + } + + fun initAnimator(minRadius: Int, maxRadius: Int, alpha: Int) { + val radiusAnimator = ObjectAnimator.ofInt(this, "RippleRadius", minRadius, maxRadius) + radiusAnimator.repeatMode = ValueAnimator.RESTART + radiusAnimator.repeatCount = ValueAnimator.INFINITE + val alphaAnimator = ObjectAnimator.ofInt(this, "RippleAlpha", alpha, 0) + alphaAnimator.repeatMode = ValueAnimator.RESTART + alphaAnimator.repeatCount = ValueAnimator.INFINITE + mAnimator = AnimatorSet() + mAnimator.playTogether(radiusAnimator, alphaAnimator) + mAnimator.duration = 1000 + mAnimator.interpolator = AccelerateInterpolator() + } +} \ No newline at end of file diff --git a/library/src/main/kotlin/com/licrafter/tagview/views/TagTextView.kt b/library/src/main/kotlin/com/licrafter/tagview/views/TagTextView.kt new file mode 100644 index 0000000..012f005 --- /dev/null +++ b/library/src/main/kotlin/com/licrafter/tagview/views/TagTextView.kt @@ -0,0 +1,34 @@ +package com.licrafter.tagview.views + +import android.content.Context +import android.graphics.Color +import android.util.AttributeSet +import android.widget.TextView +import com.licrafter.tagview.DIRECTION +import com.licrafter.tagview.utils.DipConvertUtils + +/** + * Created by lijx on 2018/5/12. + * Gmail: shellljx@gmail.com + */ +class TagTextView : TextView, ITagView { + + private var mDirection: DIRECTION? = null + + constructor(context: Context) : this(context, null) + constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0) + constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { + setTextColor(Color.WHITE) + textSize = 13f + setShadowLayer(7f, 0f, 0f, Color.BLACK) + setPadding(DipConvertUtils.dip2px(getContext(), 12f), DipConvertUtils.dip2px(getContext(), 4f), DipConvertUtils.dip2px(getContext(), 12f), DipConvertUtils.dip2px(getContext(), 4f)) + } + + override fun getDirection(): DIRECTION { + return mDirection ?: throw RuntimeException("TagTextView has no direction") + } + + override fun setDirection(direction: DIRECTION) { + mDirection = direction + } +} \ No newline at end of file diff --git a/sample/src/main/java/com/licrafter/sample/TagEditActivity.java b/sample/src/main/java/com/licrafter/sample/TagEditActivity.java index 0fb52ba..052fe02 100644 --- a/sample/src/main/java/com/licrafter/sample/TagEditActivity.java +++ b/sample/src/main/java/com/licrafter/sample/TagEditActivity.java @@ -2,8 +2,6 @@ import android.content.DialogInterface; import android.os.Bundle; -import android.support.v4.view.PagerAdapter; -import android.support.v4.view.ViewPager; import android.support.v7.app.AlertDialog; import android.support.v7.app.AppCompatActivity; import android.view.View; @@ -17,8 +15,6 @@ import com.licrafter.tagview.TagViewGroup; import com.licrafter.tagview.views.ITagView; -import java.util.Random; - /** * author: shell diff --git a/sample/src/main/java/com/licrafter/sample/utils/DataRepo.java b/sample/src/main/java/com/licrafter/sample/utils/DataRepo.java index 1ec5c6f..4846f6f 100644 --- a/sample/src/main/java/com/licrafter/sample/utils/DataRepo.java +++ b/sample/src/main/java/com/licrafter/sample/utils/DataRepo.java @@ -21,22 +21,22 @@ public static void initData() { TagGroupModel model2 = new TagGroupModel(); TagGroupModel.Tag tag0 = new TagGroupModel.Tag(); - tag0.setDirection(DIRECTION.RIGHT_TOP.getValue()); + tag0.setDirection(DirectionUtils.getValue(DIRECTION.RIGHT_TOP)); tag0.setLink("http://www.baidu.com"); tag0.setName("头层牛皮"); TagGroupModel.Tag tag1 = new TagGroupModel.Tag(); - tag1.setDirection(DIRECTION.RIGHT_CENTER.getValue()); + tag1.setDirection(DirectionUtils.getValue(DIRECTION.RIGHT_CENTER)); tag1.setLink("http://blog.licrafter.com"); tag1.setName("英伦加绒青年棕色保暖鞋子。"); TagGroupModel.Tag tag2 = new TagGroupModel.Tag(); - tag2.setDirection(DIRECTION.RIGHT_BOTTOM.getValue()); + tag2.setDirection(DirectionUtils.getValue(DIRECTION.RIGHT_BOTTOM)); tag2.setLink("http://mc.licrafter.com"); tag2.setName("PLAYBOY/花花公子"); TagGroupModel.Tag tag3 = new TagGroupModel.Tag(); - tag3.setDirection(DIRECTION.RIGHT_CENTER.getValue()); + tag3.setDirection(DirectionUtils.getValue(DIRECTION.RIGHT_CENTER)); tag3.setLink("http://mc.licrafter.com"); tag3.setName("很不错的鞋子"); diff --git a/sample/src/main/java/com/licrafter/sample/utils/DirectionUtils.java b/sample/src/main/java/com/licrafter/sample/utils/DirectionUtils.java new file mode 100644 index 0000000..6793d35 --- /dev/null +++ b/sample/src/main/java/com/licrafter/sample/utils/DirectionUtils.java @@ -0,0 +1,46 @@ +package com.licrafter.sample.utils; + +import android.util.SparseArray; + +import com.licrafter.tagview.DIRECTION; + +/** + * Created by lijx on 2018/5/12. + * Gmail: shellljx@gmail.com + */ + +public class DirectionUtils { + + private static SparseArray map; + + public static DIRECTION getDirection(int value) { + if (map == null) { + initMap(); + } + return map.get(value); + } + + public static int getValue(DIRECTION direction) { + if (map == null) { + initMap(); + } + return map.keyAt(map.indexOfValue(direction)); + } + + private static void initMap() { + map = new SparseArray<>(); + map.put(0, DIRECTION.CENTER); + map.put(1, DIRECTION.RIGHT_BOTTOM); + map.put(2, DIRECTION.RIGHT_BOTTOM_STRAIGHT); + map.put(3, DIRECTION.RIGHT_CENTER); + map.put(4, DIRECTION.RIGHT_TOP); + map.put(5, DIRECTION.RIGHT_TOP_STRAIGHT); + map.put(6, DIRECTION.LEFT_BOTTOM); + map.put(7, DIRECTION.LEFT_BOTTOM_STRAIGHT); + map.put(8, DIRECTION.LEFT_CENTER); + map.put(9, DIRECTION.LEFT_TOP); + map.put(10, DIRECTION.LEFT_TOP_STRAIGHT); + + } + +} diff --git a/sample/src/main/java/com/licrafter/sample/views/TagEditDialog.java b/sample/src/main/java/com/licrafter/sample/views/TagEditDialog.java index 72d0a96..0c432b1 100644 --- a/sample/src/main/java/com/licrafter/sample/views/TagEditDialog.java +++ b/sample/src/main/java/com/licrafter/sample/views/TagEditDialog.java @@ -3,7 +3,6 @@ import android.app.Dialog; import android.content.Context; import android.support.annotation.NonNull; -import android.support.v7.app.AlertDialog; import android.text.TextUtils; import android.view.View; import android.widget.Button; @@ -11,6 +10,7 @@ import com.licrafter.sample.R; import com.licrafter.sample.model.TagGroupModel; +import com.licrafter.sample.utils.DirectionUtils; import com.licrafter.tagview.DIRECTION; import java.util.ArrayList; @@ -70,16 +70,16 @@ public TagGroupModel createTagGroup() { private void setTagDirection(List tagList) { switch (tagList.size()) { case 3: - tagList.get(0).setDirection(DIRECTION.RIGHT_TOP.getValue()); - tagList.get(1).setDirection(DIRECTION.RIGHT_CENTER.getValue()); - tagList.get(2).setDirection(DIRECTION.RIGHT_BOTTOM.getValue()); + tagList.get(0).setDirection(DirectionUtils.getValue(DIRECTION.RIGHT_TOP)); + tagList.get(1).setDirection(DirectionUtils.getValue(DIRECTION.RIGHT_CENTER)); + tagList.get(2).setDirection(DirectionUtils.getValue(DIRECTION.RIGHT_BOTTOM)); break; case 2: - tagList.get(0).setDirection(DIRECTION.RIGHT_TOP.getValue()); - tagList.get(1).setDirection(DIRECTION.RIGHT_BOTTOM.getValue()); + tagList.get(0).setDirection(DirectionUtils.getValue(DIRECTION.RIGHT_TOP)); + tagList.get(1).setDirection(DirectionUtils.getValue(DIRECTION.RIGHT_BOTTOM)); break; case 1: - tagList.get(0).setDirection(DIRECTION.RIGHT_CENTER.getValue()); + tagList.get(0).setDirection(DirectionUtils.getValue(DIRECTION.RIGHT_CENTER)); break; } } diff --git a/sample/src/main/java/com/licrafter/sample/views/TagImageView.java b/sample/src/main/java/com/licrafter/sample/views/TagImageView.java index e33451f..c5b38a2 100644 --- a/sample/src/main/java/com/licrafter/sample/views/TagImageView.java +++ b/sample/src/main/java/com/licrafter/sample/views/TagImageView.java @@ -13,6 +13,7 @@ import com.licrafter.sample.R; import com.licrafter.sample.model.TagGroupModel; import com.licrafter.sample.utils.AnimatorUtils; +import com.licrafter.sample.utils.DirectionUtils; import com.licrafter.sample.utils.ImageLoader; import com.licrafter.tagview.DIRECTION; import com.licrafter.tagview.TagAdapter; @@ -105,7 +106,7 @@ public ITagView getItem(int position) { public TagTextView makeTagTextView(TagGroupModel.Tag tag) { TagTextView tagTextView = new TagTextView(getContext()); - tagTextView.setDirection(DIRECTION.valueOf(tag.getDirection())); + tagTextView.setDirection(DirectionUtils.getDirection(tag.getDirection())); tagTextView.setText(tag.getName()); return tagTextView; } @@ -145,7 +146,7 @@ public List getTagGroupModels() { } public void onTagClicked(TagViewGroup group, ITagView tagView, int position) { - mTagGroupModelList.get(mTagGroupViewList.indexOf(group)).getTags().get(position).setDirection(tagView.getDirection().getValue() % 10 + 1); + mTagGroupModelList.get(mTagGroupViewList.indexOf(group)).getTags().get(position).setDirection(DirectionUtils.getValue(tagView.getDirection()) % 10 + 1); group.getTagAdapter().notifyDataSetChanged(); }