From e48dff4d7b9631d435dd91ea216c82a884ebccc4 Mon Sep 17 00:00:00 2001 From: lambert Date: Mon, 9 Sep 2019 18:14:41 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8A=A0=E5=85=A53D=E8=A1=8C=E6=98=9FView?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../base/assist/ConfigConstants.java | 1 + .../widget/planetball/PlanetCalculator.java | 313 +++++++++++ .../planetball/adapter/NullPlanetAdapter.java | 33 ++ .../planetball/adapter/PlanetAdapter.java | 86 +++ .../planetball/view/PlanetBallView.java | 521 ++++++++++++++++++ .../widget/planetball/view/PlanetModel.java | 164 ++++++ .../widget/planetball/view/PlanetView.java | 253 +++++++++ base/src/main/res/values/attrs.xml | 15 + config.gradle | 1 - main/src/main/AndroidManifest.xml | 4 +- main/src/main/debug/AndroidManifest.xml | 3 + .../java/com/heyongrui/main/HomeFragment.java | 11 +- .../planetball/view/PlanetBallActivity.java | 132 +++++ .../main/res/layout/activity_planet_ball.xml | 24 + main/src/main/res/layout/fragment_home.xml | 24 + .../src/main/res/layout/activity_hitokoto.xml | 16 - user/build.gradle | 2 - 17 files changed, 1579 insertions(+), 24 deletions(-) create mode 100644 base/src/main/java/com/heyongrui/base/widget/planetball/PlanetCalculator.java create mode 100644 base/src/main/java/com/heyongrui/base/widget/planetball/adapter/NullPlanetAdapter.java create mode 100644 base/src/main/java/com/heyongrui/base/widget/planetball/adapter/PlanetAdapter.java create mode 100644 base/src/main/java/com/heyongrui/base/widget/planetball/view/PlanetBallView.java create mode 100644 base/src/main/java/com/heyongrui/base/widget/planetball/view/PlanetModel.java create mode 100644 base/src/main/java/com/heyongrui/base/widget/planetball/view/PlanetView.java create mode 100644 main/src/main/java/com/heyongrui/main/planetball/view/PlanetBallActivity.java create mode 100644 main/src/main/res/layout/activity_planet_ball.xml diff --git a/base/src/main/java/com/heyongrui/base/assist/ConfigConstants.java b/base/src/main/java/com/heyongrui/base/assist/ConfigConstants.java index ef98703..bfb060a 100644 --- a/base/src/main/java/com/heyongrui/base/assist/ConfigConstants.java +++ b/base/src/main/java/com/heyongrui/base/assist/ConfigConstants.java @@ -20,6 +20,7 @@ public class ConfigConstants { //main模块路由路径 public static final String PATH_MAIN = "/main/activity"; + public static final String PATH_PLANET_BALL = "/main/planetBall"; public static final String PATH_HOME_PROVIDER = "/home/main/service"; //user模块路由路径 public static final String PATH_KOTLIN = "/user/kotlin"; diff --git a/base/src/main/java/com/heyongrui/base/widget/planetball/PlanetCalculator.java b/base/src/main/java/com/heyongrui/base/widget/planetball/PlanetCalculator.java new file mode 100644 index 0000000..042ce6a --- /dev/null +++ b/base/src/main/java/com/heyongrui/base/widget/planetball/PlanetCalculator.java @@ -0,0 +1,313 @@ +package com.heyongrui.base.widget.planetball; + + +import com.heyongrui.base.widget.planetball.view.PlanetModel; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + + +/** + * 坐标等计算 + */ +public class PlanetCalculator { + + private static final int DEFAULT_RADIUS = 3; + private static final float[] DEFAULT_COLOR_DARK = {0.886f, 0.725f, 0.188f, 1f}; + private static final float[] DEFAULT_COLOR_LIGHT = {0.3f, 0.3f, 0.3f, 1f}; + private float maxDelta = Float.MIN_VALUE; + private float minDelta = Float.MAX_VALUE; + private List planetModelCloud; + private int radius; + private float[] tagColorLight; + private float[] tagColorDark; + private float sinAngleX, cosAngleX, sinAngleY, cosAngleY, sinAngleZ, cosAngleZ; + private float mAngleZ = 0; + private float mAngleX = 0; + private float mAngleY = 0; + /** + * 用于查找标签颜色的光谱 + */ + private int smallest, largest; + /** + * 默认设置是在云端均匀分布标签 + */ + private boolean isEvenly = true; + + public PlanetCalculator() { + this(DEFAULT_RADIUS); + } + + public PlanetCalculator(int radius) { + this(new ArrayList(), radius); + } + + public PlanetCalculator(List planetModels, int radius) { + this(planetModels, radius, DEFAULT_COLOR_DARK, DEFAULT_COLOR_LIGHT); + } + + public PlanetCalculator(List planetModels, int radius, float[] tagColorLight, float[] tagColorDark) { + this.planetModelCloud = planetModels; + this.radius = radius; + this.tagColorLight = tagColorLight; + this.tagColorDark = tagColorDark; + } + + public PlanetCalculator(List planetModels) { + this(planetModels, DEFAULT_RADIUS); + } + + public void clear() { + planetModelCloud.clear(); + } + + public List getTagList() { + return planetModelCloud; + } + + public void setTagList(List list) { + planetModelCloud = list; + } + + public PlanetModel getTop() { + int i = planetModelCloud.size() - 1; + return get(i); + } + + public PlanetModel get(int position) { + return planetModelCloud.get(position); + } + + public int indexOf(PlanetModel planetModel) { + return planetModelCloud.indexOf(planetModel); + } + + public void reset() { + create(isEvenly); + } + + /** + * 创建并初始化每个Tag的位置 + * + * @param isEvenly 是否平均分布 + */ + public void create(boolean isEvenly) { + this.isEvenly = isEvenly; + // 计算和设置每个Tag的位置 + locationAll(isEvenly); + sineCosine(mAngleX, mAngleY, mAngleZ); + updateAll(); + // 现在,让我们计算并设置每个标记的颜色: + // 首先遍历所有标记以查找最小和最大的填充 + // 权重得到t颜色2,最小的得到t颜色1,其余在中间 + smallest = 9999; + largest = 0; + for (int i = 0; i < planetModelCloud.size(); i++) { + int j = planetModelCloud.get(i).getPopularity(); + largest = Math.max(largest, j); + smallest = Math.min(smallest, j); + } + // 计算并分配颜色/文本大小 + for (int i = 0; i < planetModelCloud.size(); i++) { + initTag(planetModelCloud.get(i)); + } + } + + /** + * 计算所有的位置 + *

+ * 球坐标系(r,θ,φ)与直角坐标系(x,y,z)的转换关系: + * x=rsinθcosφ. + * y=rsinθsinφ. + * z=rcosθ. + *

+ * r -> radius + * θ -> phi + * φ -> theta + * + * @param isEvenly 是否均匀分布 + */ + private void locationAll(boolean isEvenly) { + double phi; + double theta; + int count = planetModelCloud.size(); + for (int i = 1; i < count + 1; i++) { + if (isEvenly) { + // 平均(三维直角得Z轴等分[-1,1]) θ范围[-π/2,π/2]) + phi = Math.acos(-1.0 + (2.0 * i - 1.0) / count); + theta = Math.sqrt(count * Math.PI) * phi; + } else { + phi = Math.random() * (Math.PI); + theta = Math.random() * (2 * Math.PI); + } + + planetModelCloud.get(i - 1).setLocX((float) (radius * Math.cos(theta) * Math.sin(phi))); + planetModelCloud.get(i - 1).setLocY((float) (radius * Math.sin(theta) * Math.sin(phi))); + planetModelCloud.get(i - 1).setLocZ((float) (radius * Math.cos(phi))); + } + } + + /** + * 返回角度转换成弧度之后各方向的值 + *

+ * 1度=π/180 + * + * @param mAngleX x方向旋转距离 + * @param mAngleY y方向旋转距离 + * @param mAngleZ z方向旋转距离 + */ + private void sineCosine(float mAngleX, float mAngleY, float mAngleZ) { + double degToRad = (Math.PI / 180); + sinAngleX = (float) Math.sin(mAngleX * degToRad); + cosAngleX = (float) Math.cos(mAngleX * degToRad); + sinAngleY = (float) Math.sin(mAngleY * degToRad); + cosAngleY = (float) Math.cos(mAngleY * degToRad); + sinAngleZ = (float) Math.sin(mAngleZ * degToRad); + cosAngleZ = (float) Math.cos(mAngleZ * degToRad); + } + + /** + * 更新所有的 + */ + private void updateAll() { + // 更新标签透明度和比例 + int count = planetModelCloud.size(); + for (int i = 0; i < count; i++) { + PlanetModel planetModel = planetModelCloud.get(i); + // 此部分有两个选项: + // 绕x轴旋转 + float rx1 = (planetModel.getLocX()); + float ry1 = (planetModel.getLocY()) * cosAngleX + planetModel.getLocZ() * -sinAngleX; + float rz1 = (planetModel.getLocY()) * sinAngleX + planetModel.getLocZ() * cosAngleX; + // 绕y轴旋转 + float rx2 = rx1 * cosAngleY + rz1 * sinAngleY; + float ry2 = ry1; + float rz2 = rx1 * -sinAngleY + rz1 * cosAngleY; + // 绕z轴旋转 + float rx3 = rx2 * cosAngleZ + ry2 * -sinAngleZ; + float ry3 = rx2 * sinAngleZ + ry2 * cosAngleZ; + float rz3 = rz2; + // 将数组设置为新位置 + planetModel.setLocX(rx3); + planetModel.setLocY(ry3); + planetModel.setLocZ(rz3); + + // 添加透视图 + int diameter = 2 * radius; + float per = diameter / (diameter + rz3); + // 让我们为标签设置位置、比例和透明度 + planetModel.setLoc2DX(rx3); + planetModel.setLoc2DY(ry3); + planetModel.setScale(per); + + // 计算透明度 + float delta = diameter + rz3; + maxDelta = Math.max(maxDelta, delta); + minDelta = Math.min(minDelta, delta); + float alpha = (delta - minDelta) / (maxDelta - minDelta); + planetModel.setAlpha(1 - alpha); + } + sortTagByScale(); + } + + private void initTag(PlanetModel planetModel) { + float percentage = getPercentage(planetModel); + float[] argb = getColorFromGradient(percentage); + planetModel.setColorByArray(argb); + } + + /** + * 根据缩放值排序 + */ + public void sortTagByScale() { + Collections.sort(planetModelCloud, new TagComparator()); + } + + private float getPercentage(PlanetModel planetModel) { + int p = planetModel.getPopularity(); + return (smallest == largest) ? 1.0f : ((float) p - smallest) / ((float) largest - smallest); + } + + private float[] getColorFromGradient(float percentage) { + float[] rgba = new float[4]; + rgba[0] = 1f; + rgba[1] = (percentage * (tagColorDark[0])) + ((1f - percentage) * (tagColorLight[0])); + rgba[2] = (percentage * (tagColorDark[1])) + ((1f - percentage) * (tagColorLight[1])); + rgba[3] = (percentage * (tagColorDark[2])) + ((1f - percentage) * (tagColorLight[2])); + return rgba; + } + + /** + * 更新所有元素的透明度/比例 + */ + public void update() { + // 如果mAngleX和mAngleY低于阈值,则跳过运动计算以获得性能 + if (Math.abs(mAngleX) > .1 || Math.abs(mAngleY) > .1) { + sineCosine(mAngleX, mAngleY, mAngleZ); + updateAll(); + } + } + + /** + * 添加单个标签 + * + * @param planetModel 标签 + */ + public void add(PlanetModel planetModel) { + initTag(planetModel); + location(planetModel); + planetModelCloud.add(planetModel); + updateAll(); + } + + /** + * 添加新标签时,只需将其放置在某个随机位置 + * 在多次添加之后,执行一次重置以重新排列所有标记 + * + * @param planetModel 标签 + */ + private void location(PlanetModel planetModel) { + double phi; + double theta; + phi = Math.random() * (Math.PI); + theta = Math.random() * (2 * Math.PI); + planetModel.setLocX((int) (radius * Math.cos(theta) * Math.sin(phi))); + planetModel.setLocY((int) (radius * Math.sin(theta) * Math.sin(phi))); + planetModel.setLocZ((int) (radius * Math.cos(phi))); + } + + /** + * 设置半径 + * + * @param radius 半径 + */ + public void setRadius(int radius) { + this.radius = radius; + } + + public void setTagColorLight(float[] tagColor) { + this.tagColorLight = tagColor; + } + + public void setTagColorDark(float[] tagColorDark) { + this.tagColorDark = tagColorDark; + } + + public void setAngleX(float mAngleX) { + this.mAngleX = mAngleX; + } + + public void setAngleY(float mAngleY) { + this.mAngleY = mAngleY; + } + + private static class TagComparator implements Comparator { + + @Override + public int compare(PlanetModel planetModel1, PlanetModel planetModel2) { + return planetModel1.getScale() > planetModel2.getScale() ? 1 : 0; + } + } +} diff --git a/base/src/main/java/com/heyongrui/base/widget/planetball/adapter/NullPlanetAdapter.java b/base/src/main/java/com/heyongrui/base/widget/planetball/adapter/NullPlanetAdapter.java new file mode 100644 index 0000000..ef56525 --- /dev/null +++ b/base/src/main/java/com/heyongrui/base/widget/planetball/adapter/NullPlanetAdapter.java @@ -0,0 +1,33 @@ +package com.heyongrui.base.widget.planetball.adapter; + +import android.content.Context; +import android.view.View; +import android.view.ViewGroup; + +public class NullPlanetAdapter extends PlanetAdapter { + + @Override + public int getCount() { + return 0; + } + + @Override + public View getView(Context context, int position, ViewGroup parent) { + return null; + } + + @Override + public Object getItem(int position) { + return null; + } + + @Override + public int getPopularity(int position) { + return 0; + } + + @Override + public void onThemeColorChanged(View view, int themeColor) { + + } +} diff --git a/base/src/main/java/com/heyongrui/base/widget/planetball/adapter/PlanetAdapter.java b/base/src/main/java/com/heyongrui/base/widget/planetball/adapter/PlanetAdapter.java new file mode 100644 index 0000000..ab02a56 --- /dev/null +++ b/base/src/main/java/com/heyongrui/base/widget/planetball/adapter/PlanetAdapter.java @@ -0,0 +1,86 @@ +package com.heyongrui.base.widget.planetball.adapter; + +import android.content.Context; +import android.view.View; +import android.view.ViewGroup; + +/** + * 3D行星球适配器 + */ +public abstract class PlanetAdapter { + + /** + * 数据改变监听 + */ + private OnDataSetChangeListener onDataSetChangeListener; + + /** + * 星球(标签)个数 + * + * @return 星球(标签)个数 + */ + public abstract int getCount(); + + /** + * 获取标签的View + * + * @param context 上下文 + * @param position 位置 + * @param parent 父布局 + * @return 标签的View + */ + public abstract View getView(Context context, int position, ViewGroup parent); + + /** + * 获取Item + * + * @param position 位置 + * @return Item + */ + public abstract Object getItem(int position); + + /** + * 获取标签的权重 + * + * @param position 位置 + * @return 标签的权重 + */ + public abstract int getPopularity(int position); + + /** + * 主题颜色改变 + * + * @param view 视图 + * @param themeColor 主题色 + */ + public abstract void onThemeColorChanged(View view, int themeColor); + + /** + * 数据更新 + */ + public final void notifyDataSetChanged() { + if (onDataSetChangeListener == null) { + return; + } + onDataSetChangeListener.onChange(); + } + + /** + * 设置数据改变监听 + * + * @param listener 数据改变监听器 + */ + public void setOnDataSetChangeListener(OnDataSetChangeListener listener) { + onDataSetChangeListener = listener; + } + + /** + * 数据改变监听器 + */ + public interface OnDataSetChangeListener { + /** + * 数据改变 + */ + void onChange(); + } +} \ No newline at end of file diff --git a/base/src/main/java/com/heyongrui/base/widget/planetball/view/PlanetBallView.java b/base/src/main/java/com/heyongrui/base/widget/planetball/view/PlanetBallView.java new file mode 100644 index 0000000..78fab78 --- /dev/null +++ b/base/src/main/java/com/heyongrui/base/widget/planetball/view/PlanetBallView.java @@ -0,0 +1,521 @@ +package com.heyongrui.base.widget.planetball.view; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Color; +import android.graphics.Point; +import android.os.Handler; +import android.os.Looper; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewConfiguration; +import android.view.ViewGroup; +import android.view.WindowManager; + +import androidx.annotation.IntDef; + +import com.heyongrui.base.R; +import com.heyongrui.base.widget.planetball.PlanetCalculator; +import com.heyongrui.base.widget.planetball.adapter.NullPlanetAdapter; +import com.heyongrui.base.widget.planetball.adapter.PlanetAdapter; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * 3D行星球 + */ +public class PlanetBallView extends ViewGroup implements Runnable, PlanetAdapter.OnDataSetChangeListener { + + public static final float MAX_SCALE = 1.3f; + public static final float MINI_SCALE = 1f; + public static final int MODE_DISABLE = 0; + public static final int MODE_DECELERATE = 1; + public static final int MODE_UNIFORM = 2; + private static final float TOUCH_SCALE_FACTOR = 1f; + private static final float TRACKBALL_SCALE_FACTOR = 10; + public int mode; + private float speed = 4f; + private PlanetCalculator mPlanetCalculator; + private float mAngleX; + private float mAngleY; + private float centerX, centerY; + private float radius; + /** + * 半径的百分比 + */ + private float radiusPercent = 0.9f; + private float[] darkColor = new float[]{1.0f, 0.0f, 0.0f, 1.0f}; + private float[] lightColor = new float[]{0.9412f, 0.7686f, 0.2f, 1.0f}; + /** + * 是否支持手动滑动 + */ + private boolean manualScroll; + private MarginLayoutParams layoutParams; + private int minSize; + private boolean isOnTouch = false; + private Handler handler = new Handler(Looper.getMainLooper()); + private PlanetAdapter planetAdapter = new NullPlanetAdapter(); + private OnTagClickListener onTagClickListener; + private float downX, downY; + private float scaleX; + private float startDistance; + private boolean multiplePointer; + private float startX; + private float startY; + + public PlanetBallView(Context context) { + super(context); + init(context, null); + } + + private void init(Context context, AttributeSet attrs) { + setFocusableInTouchMode(true); + mPlanetCalculator = new PlanetCalculator(); + if (attrs != null) { + TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.PlanetBallView); + mode = typedArray.getInteger(R.styleable.PlanetBallView_autoScrollMode, MODE_DISABLE); + setManualScroll(typedArray.getBoolean(R.styleable.PlanetBallView_manualScroll, true)); + mAngleX = typedArray.getFloat(R.styleable.PlanetBallView_startAngleX, 0.5f); + mAngleY = typedArray.getFloat(R.styleable.PlanetBallView_startAngleY, 0.5f); + setLightColor(typedArray.getColor(R.styleable.PlanetBallView_lightColor, Color.WHITE)); + setDarkColor(typedArray.getColor(R.styleable.PlanetBallView_darkColor, Color.BLACK)); + setRadiusPercent(typedArray.getFloat(R.styleable.PlanetBallView_radiusPercent, radiusPercent)); + setScrollSpeed(typedArray.getFloat(R.styleable.PlanetBallView_scrollSpeed, 2f)); + typedArray.recycle(); + } + WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE); + Point point = new Point(); + if (wm != null) { + wm.getDefaultDisplay().getSize(point); + } + int screenWidth = point.x; + int screenHeight = point.y; + minSize = screenHeight < screenWidth ? screenHeight : screenWidth; + + initFromAdapter(); + } + + public void setManualScroll(boolean manualScroll) { + this.manualScroll = manualScroll; + } + + public void setLightColor(int color) { + lightColor = new float[]{ + Color.alpha(color) / 1.0f / 0xff, + Color.red(color) / 1.0f / 0xff, + Color.green(color) / 1.0f / 0xff, + Color.blue(color) / 1.0f / 0xff} + .clone(); + onChange(); + } + + public void setDarkColor(int color) { + darkColor = new float[]{ + Color.alpha(color) / 1.0f / 0xff, + Color.red(color) / 1.0f / 0xff, + Color.green(color) / 1.0f / 0xff, + Color.blue(color) / 1.0f / 0xff} + .clone(); + onChange(); + } + + public void setRadiusPercent(float percent) { + if (percent > 1 || percent < 0) { + throw new IllegalArgumentException("Percent value not in range 0 to 1."); + } else { + radiusPercent = percent; + onChange(); + } + } + + public void setScrollSpeed(float scrollSpeed) { + speed = scrollSpeed; + } + + /** + * 初始化VIew根据Adapter + */ + public void initFromAdapter() { + this.post(new Runnable() { + @Override + public void run() { + // 中心坐标 + centerX = (getRight() - getLeft()) / 2f; + centerY = (getBottom() - getTop()) / 2f; + // 半径 + radius = Math.min(centerX, centerY) * radiusPercent; + mPlanetCalculator.setRadius((int) radius); + + mPlanetCalculator.setTagColorLight(lightColor); + mPlanetCalculator.setTagColorDark(darkColor); + + mPlanetCalculator.clear(); + + for (int i = 0; i < planetAdapter.getCount(); i++) { + // 为每个Tag绑定View + PlanetModel planetModel = new PlanetModel(planetAdapter.getPopularity(i)); + View view = planetAdapter.getView(getContext(), i, PlanetBallView.this); + planetModel.setView(view); + mPlanetCalculator.add(planetModel); + // 点击事件监听 + addListener(view, i); + } + mPlanetCalculator.create(true); + mPlanetCalculator.setAngleX(mAngleX); + mPlanetCalculator.setAngleY(mAngleY); + mPlanetCalculator.update(); + + resetChildren(); + } + }); + } + + @Override + public void onChange() { + post(this); + } + + private void addListener(View view, final int position) { + if (!view.hasOnClickListeners() && onTagClickListener != null) { + view.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + onTagClickListener.onItemClick(PlanetBallView.this, v, position); + } + }); + } + } + + /** + * 重新设置子View + */ + private void resetChildren() { + removeAllViews(); + // 必须保证getChildAt(i) == mTagCloud.getTagList().get(i) + for (PlanetModel planetModel : mPlanetCalculator.getTagList()) { + addView(planetModel.getView()); + } + } + + public PlanetBallView(Context context, AttributeSet attrs) { + super(context, attrs); + init(context, attrs); + } + + public PlanetBallView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(context, attrs); + } + + @Mode + public int getAutoScrollMode() { + return this.mode; + } + + /** + * 设置滚动模式 + * + * @param mode 滚动模式 + */ + public void setAutoScrollMode(@Mode int mode) { + this.mode = mode; + } + + /** + * 谁知适配器 + * + * @param adapter 适配器 + */ + public final void setAdapter(PlanetAdapter adapter) { + planetAdapter = adapter; + planetAdapter.setOnDataSetChangeListener(this); + onChange(); + } + + public void reset() { + mPlanetCalculator.reset(); + resetChildren(); + } + + @Override + public boolean onTrackballEvent(MotionEvent e) { + if (manualScroll) { + float x = e.getX(); + float y = e.getY(); + + mAngleX = (y) * speed * TRACKBALL_SCALE_FACTOR; + mAngleY = (-x) * speed * TRACKBALL_SCALE_FACTOR; + + mPlanetCalculator.setAngleX(mAngleX); + mPlanetCalculator.setAngleY(mAngleY); + mPlanetCalculator.update(); + + resetChildren(); + } + return true; + } + + @Override + public boolean onTouchEvent(MotionEvent e) { + if (manualScroll) { + handleTouchEvent(e); + } + return true; + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + + int contentWidth = MeasureSpec.getSize(widthMeasureSpec); + int contentHeight = MeasureSpec.getSize(heightMeasureSpec); + int widthMode = MeasureSpec.getMode(widthMeasureSpec); + int heightMode = MeasureSpec.getMode(heightMeasureSpec); + + if (layoutParams == null) { + layoutParams = (MarginLayoutParams) getLayoutParams(); + } + + int dimensionX = widthMode == MeasureSpec.EXACTLY ? contentWidth : minSize - layoutParams.leftMargin - layoutParams.rightMargin; + int dimensionY = heightMode == MeasureSpec.EXACTLY ? contentHeight : minSize - layoutParams.leftMargin - layoutParams.rightMargin; + setMeasuredDimension(dimensionX, dimensionY); + + measureChildren(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED); + } + + /** + * 处理触摸事件 + */ + private boolean handleTouchEvent(MotionEvent event) { + // 触摸点个数 + int pointerCount = event.getPointerCount(); + if (pointerCount > 1) { + multiplePointer = true; + } + int action = event.getActionMasked(); + switch (action) { + case MotionEvent.ACTION_DOWN: + isOnTouch = true; + downX = event.getX(); + downY = event.getY(); + startX = downX; + startY = downY; + break; + case MotionEvent.ACTION_POINTER_DOWN: + if (event.getActionIndex() == 1) { + // 第二个触摸点 + scaleX = getScaleX(); + startDistance = distance(event.getX(0) - event.getX(1), + event.getY(0) - event.getY(1)); + return true; + } + break; + case MotionEvent.ACTION_MOVE: + if (pointerCount == 1 && !multiplePointer) { + // 单点触摸,旋转星球 + float dx = event.getX() - downX; + float dy = event.getY() - downY; + if (isValidMove(dx, dy)) { + mAngleX = (dy / radius) * speed * TOUCH_SCALE_FACTOR; + mAngleY = (-dx / radius) * speed * TOUCH_SCALE_FACTOR; + processTouch(); + downX = event.getX(); + downY = event.getY(); + } + return isValidMove(downX - startX, downY - startY); + } else if (pointerCount == 2) { + // 双点触摸,缩放 + float endDistance = distance(event.getX(0) - event.getX(1), + event.getY(0) - event.getY(1)); + // 缩放比例 + float scale = ((endDistance - startDistance) / (endDistance * 2) + 1) * scaleX; + if (scale > MAX_SCALE) { + scale = MAX_SCALE; + } + if (scale < MINI_SCALE) { + scale = MINI_SCALE; + } + setScaleX(scale); + setScaleY(scale); + return true; + } + break; + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: + multiplePointer = false; + isOnTouch = false; + break; + default: + break; + } + return false; + } + + /** + * 两点之间的距离 + * + * @param x x轴距离 + * @param y y轴距离 + * @return 两点之间的距离 + */ + private float distance(float x, float y) { + return (float) Math.sqrt(x * x + y * y); + } + + /** + * 是否是有效移动 + * + * @param dx x轴位移 + * @param dy y轴位移 + * @return 是否是有效移动 + */ + private boolean isValidMove(float dx, float dy) { + int minDistance = ViewConfiguration.get(getContext()).getScaledTouchSlop(); + return (Math.abs(dx) > minDistance || Math.abs(dy) > minDistance); + } + + /** + * 更新视图 + */ + private void processTouch() { + // 设置旋转的X,Y + if (mPlanetCalculator != null) { + mPlanetCalculator.setAngleX(mAngleX); + mPlanetCalculator.setAngleY(mAngleY); + mPlanetCalculator.update(); + } + for (int i = 0; i < getChildCount(); i++) { + PlanetModel planetModel = mPlanetCalculator.get(i); + View child = planetModel.getView(); + // 更新每一个ChildView + if (child != null && child.getVisibility() != GONE) { + planetAdapter.onThemeColorChanged(child, planetModel.getColor()); + // 缩放小于1的设置不可点击 + if (planetModel.getScale() < 1.0f) { + child.setScaleX(planetModel.getScale()); + child.setScaleY(planetModel.getScale()); + child.setClickable(false); + } else { + child.setClickable(true); + } + // 设置透明度 + child.setAlpha(planetModel.getScale()); + int left = (int) (centerX + planetModel.getLoc2DX()) - child.getMeasuredWidth() / 2; + int top = (int) (centerY + planetModel.getLoc2DY()) - child.getMeasuredHeight() / 2; + // 从View的Tag里取出位置之前的位置信息,平移新旧位置差值 + int[] originLocation = (int[]) child.getTag(); + if (originLocation != null && originLocation.length > 0) { + child.setTranslationX((float) (left - originLocation[0])); + child.setTranslationY((float) (top - originLocation[1])); + // 小于移动速度,刷新 + if (Math.abs(mAngleX) <= speed && Math.abs(mAngleY) <= speed) { + child.invalidate(); + } + } + } + } + } + + @Override + public boolean onInterceptTouchEvent(MotionEvent ev) { + if (manualScroll) { + return handleTouchEvent(ev); + } + return false; + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + handler.post(this); + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + handler.removeCallbacksAndMessages(null); + } + + @SuppressLint("DrawAllocation") + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + for (int i = 0; i < getChildCount(); i++) { + View child = getChildAt(i); + PlanetModel planetModel = mPlanetCalculator.get(i); + if (child != null && child.getVisibility() != GONE) { + planetAdapter.onThemeColorChanged(child, planetModel.getColor()); + // 设置缩放 + if (planetModel.getScale() < 1f) { + child.setScaleX(planetModel.getScale()); + child.setScaleY(planetModel.getScale()); + } + // 设置透明度 + child.setAlpha(planetModel.getScale()); + // 设置位置 + int left = (int) (centerX + planetModel.getLoc2DX()) - child.getMeasuredWidth() / 2; + int top = (int) (centerY + planetModel.getLoc2DY()) - child.getMeasuredHeight() / 2; + + child.layout(left, top, left + child.getMeasuredWidth(), top + child.getMeasuredHeight()); + // 设置位置信息的TAG + child.setTag(new int[]{left, top}); + } + } + } + + /** + * 设置标签点击事件监听 + */ + public void setOnTagClickListener(OnTagClickListener listener) { + onTagClickListener = listener; + } + + @Override + public void run() { + // 非用户触摸状态,和非不可滚动状态 + if (!isOnTouch && mode != MODE_DISABLE) { + // 减速模式(均速衰减) + if (mode == MODE_DECELERATE) { + if (Math.abs(mAngleX) > 0.2f) { + mAngleX -= mAngleX * 0.1f; + } + if (Math.abs(mAngleY) > 0.2f) { + mAngleY -= mAngleY * 0.1f; + } + } + processTouch(); + } + handler.removeCallbacksAndMessages(null); + // 延时 + handler.postDelayed(this, 30); + } + + /** + * 滚动模式 + *

+ * {@link #MODE_DISABLE} 不让滚动 + * {@link #MODE_DECELERATE} 减速模式 + * {@link #MODE_UNIFORM} 匀速模式 + */ + @IntDef({MODE_DISABLE, MODE_DECELERATE, MODE_UNIFORM}) + @Retention(RetentionPolicy.SOURCE) + public @interface Mode { + } + + /** + * TAG的点击事件 + */ + public interface OnTagClickListener { + + /** + * TAG点击 + * + * @param parent parent + * @param view view + * @param position position + */ + void onItemClick(ViewGroup parent, View view, int position); + } +} \ No newline at end of file diff --git a/base/src/main/java/com/heyongrui/base/widget/planetball/view/PlanetModel.java b/base/src/main/java/com/heyongrui/base/widget/planetball/view/PlanetModel.java new file mode 100644 index 0000000..6339f33 --- /dev/null +++ b/base/src/main/java/com/heyongrui/base/widget/planetball/view/PlanetModel.java @@ -0,0 +1,164 @@ +package com.heyongrui.base.widget.planetball.view; + +import android.graphics.Color; +import android.view.View; + +/** + * 星球属性实体 + */ +public class PlanetModel { + + /** + * 默认权重 + */ + private static final int DEFAULT_POPULARITY = 5; + /** + * 权重 + */ + private int popularity; + /** + * 3D坐标位置 + */ + private float locX, locY, locZ; + /** + * 2D坐标位置 + */ + private float loc2DX, loc2DY; + /** + * 缩放比 + */ + private float scale; + /** + * 透明度 + */ + private float alpha; + /** + * 颜色 + */ + private float[] argb; + /** + * View + */ + private View mView; + + + public PlanetModel() { + this(0f, 0f, 0f, 1.0f, 0); + } + + public PlanetModel(float locX, float locY, float locZ, float scale, int popularity) { + this.locX = locX; + this.locY = locY; + this.locZ = locZ; + + this.loc2DX = 0; + this.loc2DY = 0; + + this.argb = new float[]{1.0f, 0.5f, 0.5f, 0.5f}; + + this.scale = scale; + this.popularity = popularity; + } + + public PlanetModel(int popularity) { + this(0f, 0f, 0f, 1.0f, popularity); + } + + public PlanetModel(float locX, float locY, float locZ) { + this(locX, locY, locZ, 1.0f, DEFAULT_POPULARITY); + } + + public PlanetModel(float locX, float locY, float locZ, float scale) { + this(locX, locY, locZ, scale, DEFAULT_POPULARITY); + } + + public float getLocX() { + return locX; + } + + public void setLocX(float locX) { + this.locX = locX; + } + + public float getLocY() { + return locY; + } + + public void setLocY(float locY) { + this.locY = locY; + } + + public float getLocZ() { + return locZ; + } + + public void setLocZ(float locZ) { + this.locZ = locZ; + } + + public float getScale() { + return scale; + } + + public void setScale(float scale) { + this.scale = scale; + if (this.mView != null) { + ((PlanetView) this.mView).setScale(scale); + } + } + + public View getView() { + return mView; + } + + public void setView(View view) { + this.mView = view; + } + + public float getAlpha() { + return alpha; + } + + public void setAlpha(float alpha) { + this.alpha = alpha; + this.argb[0] = alpha; + } + + public int getPopularity() { + return popularity; + } + + public void setPopularity(int popularity) { + this.popularity = popularity; + } + + public float getLoc2DX() { + return loc2DX; + } + + public void setLoc2DX(float loc2dx) { + loc2DX = loc2dx; + } + + public float getLoc2DY() { + return loc2DY; + } + + public void setLoc2DY(float loc2dy) { + loc2DY = loc2dy; + } + + public void setColorByArray(float[] rgb) { + if (rgb != null) { + System.arraycopy(rgb, 0, this.argb, this.argb.length - rgb.length, rgb.length); + } + } + + public int getColor() { + int[] result = new int[4]; + for (int i = 0; i < 4; i++) { + result[i] = (int) (this.argb[i] * 0xff); + } + return Color.argb(result[0], result[1], result[2], result[3]); + } +} \ No newline at end of file diff --git a/base/src/main/java/com/heyongrui/base/widget/planetball/view/PlanetView.java b/base/src/main/java/com/heyongrui/base/widget/planetball/view/PlanetView.java new file mode 100644 index 0000000..2371b6a --- /dev/null +++ b/base/src/main/java/com/heyongrui/base/widget/planetball/view/PlanetView.java @@ -0,0 +1,253 @@ +package com.heyongrui.base.widget.planetball.view; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.LinearGradient; +import android.graphics.Paint; +import android.graphics.Rect; +import android.graphics.Shader.TileMode; +import android.util.AttributeSet; +import android.view.View; + + +/** + * 星球(单个) + */ +public class PlanetView extends View { + + public static final int COLOR_MALE = 0xC1F1F2; + public static final int COLOR_FEMALE = 0xFBD8D8; + public static final int COLOR_MOST_ACTIVE = 0xFFFFFF; + public static final int COLOR_MOST_NEW = 0x9485D8; + public static final int COLOR_BEST_MATCH = 0x58B4AC; + private float shadowRadius = -1.0f; + private boolean isEnlarge = false; + private float radiusIncrement = 1.0f; + private float signY; + private float signWidth; + private float totalSignWidth = 0.0f; + private float maxSignRange = 0.0f; + private float signX; + private float matchPercentX; + private float matchPercentY; + private float matchDescribeX; + private float matchDescribeY; + private float signDistanceX = 5.0f; + private Paint starPaint; + private Paint signPaint; + private Paint otherPaint; + private Paint matchPaint; + private int starWidth; + private int starMarginTop; + private int signTextSize; + private int matchTextSize; + private float scale; + private int starColor; + private int matchColor; + private boolean isOverstep; + private String sign; + private String matchPercent; + private String matchDescribe; + private boolean hasShadow; + private float starCenterX; + private float starCenterY; + private float starRadius; + private float starMin; + + public PlanetView(Context context) { + super(context); + init(context); + } + + private void init(Context context) { + starPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + starPaint.setColor(0xFFFF0000); + signTextSize = sp2px(context, 9.0f); + signPaint = new Paint(Paint.HINTING_ON); + signPaint.setColor(0x7FEEEEEE); + signPaint.setTextSize((float) signTextSize); + otherPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + otherPaint.setColor(0x7FEEEEEE); + otherPaint.setTextSize((float) sp2px(context, 9.0f)); + matchPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + matchPaint.setColor(0xFFFFFFFF); + matchTextSize = sp2px(context, 6.0f); + matchPaint.setTextSize((float) matchTextSize); + setLayerType(LAYER_TYPE_SOFTWARE, null); + int startX = sp2px(context, 50.0f); + starWidth = sp2px(context, 20.0f); + starMarginTop = 0; + signPaint.setShader(new LinearGradient((float) startX, 0.0f, 0.0f, 0.0f, + new int[]{0x33333333, 0xFFFFFFFF, 0xFFFFFFFF, 0x33333333}, + new float[]{0.0f, 0.15f, 0.85f, 1.0f}, TileMode.CLAMP)); + otherPaint.setShader(new LinearGradient((float) startX, 0.0f, 0.0f, 0.0f, + new int[]{0x33333333, 0xFF888888, 0xFF888888, 0x33333333}, + new float[]{0.0f, 0.15f, 0.85f, 1.0f}, TileMode.CLAMP)); + starMin = starWidth * 3.0f / 4.0f; + starRadius = starWidth - starMin; + starMin -= 3.0f; + shadowRadius = starMin; + radiusIncrement = starMin / 16.0f; + } + + public PlanetView(Context context, AttributeSet attributeSet) { + super(context, attributeSet); + init(context); + } + + public PlanetView(Context context, AttributeSet attributeSet, int i) { + super(context, attributeSet, i); + init(context); + } + + public void setScale(float scale) { + this.scale = scale; + } + + /** + * 设置星星颜色 + * + * @param color 颜色 + */ + public void setStarColor(int color) { + this.starColor = color; + } + + /** + * 设置匹配颜色 + * + * @param color 颜色 + */ + public void setMatchColor(int color) { + this.matchColor = color; + } + + /** + * 设置名字 + * + * @param sign 名字 + */ + public void setSign(String sign) { + this.sign = sign; + } + + /** + * 设置匹配显示 + * + * @param matchPercent 匹配百分比文字 + * @param matchDescribe 匹配描述(最匹配、最活跃) + */ + public void setMatch(String matchPercent, String matchDescribe) { + this.matchPercent = matchPercent; + this.matchDescribe = matchDescribe; + } + + /** + * 设置是否有阴影 + * + * @param hasShadow 是否有阴影 + */ + public void setHasShadow(boolean hasShadow) { + this.hasShadow = hasShadow; + } + + @Override + protected void onSizeChanged(int w, int h, int oldw, int oldh) { + super.onSizeChanged(w, h, oldw, oldh); + signY = (float) (getPaddingTop() + signTextSize); + signWidth = signPaint.measureText(sign); + if (signWidth > w) { + isOverstep = true; + totalSignWidth = signWidth + w; + maxSignRange = (w + signWidth) + signWidth; + signDistanceX = totalSignWidth; + } else { + signX = (w - signWidth) / 2.0f; + } + starCenterX = (float) (w / 2); + starCenterY = (signY + ((float) starMarginTop)) + ((float) (starWidth / 2)); + Rect matchRect = new Rect(); + matchPaint.getTextBounds(matchPercent, 0, matchPercent.length(), matchRect); + matchPercentX = (float) ((w - matchRect.width()) / 2); + matchPercentY = (((signY + ((float) starMarginTop)) + ((float) starWidth)) + ((float) starMarginTop)) + ((float) matchRect.height()); + matchPaint.getTextBounds(matchDescribe, 0, matchDescribe.length(), matchRect); + matchDescribeX = (float) ((w - matchRect.width()) / 2); + matchDescribeY = (matchPercentY + ((float) this.starMarginTop)) + ((float) matchRect.height()); + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + float radius = starRadius; + float min = Math.min(scale * 0.5f, 1.0f); + // 透明度 + int alpha = (int) (0xff * min); + // 半径 + radius *= min; + // 设置昵称颜色 + signPaint.setARGB(alpha, 238, 238, 238); + // 昵称文字过长(跑马灯) + if (isOverstep) { + canvas.drawText(sign, totalSignWidth - signDistanceX, signY, signPaint); + } else { + canvas.drawText(sign, signX, signY, signPaint); + } + // 星星球颜色(透明度) + int color = starColor | alpha << 24; + starPaint.setColor(color); + // 是否有阴影 + if (hasShadow) { + starPaint.setShadowLayer(shadowRadius, 1.0f, 1.0f, color); + canvas.drawCircle(starCenterX, starCenterY, radius, starPaint); + canvas.drawCircle(starCenterX, starCenterY, radius, starPaint); + } + canvas.drawCircle(starCenterX, starCenterY, radius, starPaint); + matchPaint.setColor(alpha << 24 | matchColor); + canvas.drawText(matchPercent, matchPercentX, matchPercentY, matchPaint); + canvas.drawText(matchDescribe, matchDescribeX, matchDescribeY, matchPaint); + if (hasShadow || isOverstep) { + if (isOverstep) { + signDistanceX = signDistanceX + 0.5f; + if (signDistanceX > maxSignRange) { + signDistanceX = signWidth; + } + } + if (hasShadow) { + if (isEnlarge) { + shadowRadius += radiusIncrement; + } else { + shadowRadius -= radiusIncrement; + } + if (shadowRadius < 1) { + shadowRadius = 1.0f; + isEnlarge = true; + } else if (shadowRadius > radius) { + shadowRadius = radius; + isEnlarge = false; + } + } + } + } + + /** + * dp转px + * + * @param context 上下文 + * @param dp dp值 + * @return px值 + */ + private int dp2px(Context context, float dp) { + return (int) ((dp * context.getResources().getDisplayMetrics().density) + 0.5f); + } + + /** + * sp转px + * + * @param context 上下文 + * @param sp sp值 + * @return px值 + */ + private int sp2px(Context context, float sp) { + return (int) ((sp * context.getResources().getDisplayMetrics().scaledDensity) + 0.5f); + } +} \ No newline at end of file diff --git a/base/src/main/res/values/attrs.xml b/base/src/main/res/values/attrs.xml index f3f7c67..20c1464 100644 --- a/base/src/main/res/values/attrs.xml +++ b/base/src/main/res/values/attrs.xml @@ -95,4 +95,19 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/config.gradle b/config.gradle index 401712b..b9816f4 100644 --- a/config.gradle +++ b/config.gradle @@ -40,7 +40,6 @@ ext { immersionbar : '3.0.0-beta05', baseRlvAdapter : '2.9.49-androidx', smartRefreshLayout: '1.1.0-andx-15', - tagcloudlib : '1.2.0', jiaozivideoplayer : '7.0.4', ahbottomnavigation: '2.3.4', banner : '2.3.16', diff --git a/main/src/main/AndroidManifest.xml b/main/src/main/AndroidManifest.xml index 4869b4d..32ec01d 100644 --- a/main/src/main/AndroidManifest.xml +++ b/main/src/main/AndroidManifest.xml @@ -5,6 +5,8 @@ - + diff --git a/main/src/main/debug/AndroidManifest.xml b/main/src/main/debug/AndroidManifest.xml index fd22d6d..9b3f8b1 100644 --- a/main/src/main/debug/AndroidManifest.xml +++ b/main/src/main/debug/AndroidManifest.xml @@ -18,6 +18,9 @@ + diff --git a/main/src/main/java/com/heyongrui/main/HomeFragment.java b/main/src/main/java/com/heyongrui/main/HomeFragment.java index f2efcb7..563193d 100644 --- a/main/src/main/java/com/heyongrui/main/HomeFragment.java +++ b/main/src/main/java/com/heyongrui/main/HomeFragment.java @@ -2,7 +2,7 @@ import android.os.Bundle; import android.view.View; -import android.widget.TextView; +import android.widget.Button; import com.alibaba.android.arouter.launcher.ARouter; import com.heyongrui.base.assist.ConfigConstants; @@ -38,8 +38,10 @@ protected int getLayoutId() { @Override public void onClick(View v) { int id = v.getId(); - if (id == R.id.tv_home) { + if (id == R.id.btn_motion) { ARouter.getInstance().build(ConfigConstants.PATH_KOTLIN).navigation(); + } else if (id == R.id.btn_planet_ball) { + ARouter.getInstance().build(ConfigConstants.PATH_PLANET_BALL).navigation(); } else if (id == R.id.ticker_view) { if (tickerCount < 5) { tickerView.setCharacterLists(TickerUtils.provideNumberList()); @@ -63,9 +65,10 @@ public void onClick(View v) { protected void initView(Bundle savedInstanceState) { tickerView = mView.findViewById(R.id.ticker_view); numberRunTv = mView.findViewById(R.id.number_run_tv); - TextView tvHome = mView.findViewById(R.id.tv_home); + Button btnMotion = mView.findViewById(R.id.btn_motion); + Button btnPlanetBall = mView.findViewById(R.id.btn_planet_ball); - addOnClickListeners(this, tvHome, tickerView, numberRunTv); + addOnClickListeners(this, tickerView, numberRunTv, btnMotion, btnPlanetBall); } @Override diff --git a/main/src/main/java/com/heyongrui/main/planetball/view/PlanetBallActivity.java b/main/src/main/java/com/heyongrui/main/planetball/view/PlanetBallActivity.java new file mode 100644 index 0000000..2850350 --- /dev/null +++ b/main/src/main/java/com/heyongrui/main/planetball/view/PlanetBallActivity.java @@ -0,0 +1,132 @@ +package com.heyongrui.main.planetball.view; + +import android.content.Context; +import android.os.Bundle; +import android.view.View; +import android.view.ViewGroup; + +import com.alibaba.android.arouter.facade.annotation.Route; +import com.blankj.utilcode.util.ConvertUtils; +import com.heyongrui.base.assist.ConfigConstants; +import com.heyongrui.base.base.BaseActivity; +import com.heyongrui.base.widget.planetball.adapter.PlanetAdapter; +import com.heyongrui.base.widget.planetball.view.PlanetBallView; +import com.heyongrui.base.widget.planetball.view.PlanetView; +import com.heyongrui.main.R; + +import java.io.UnsupportedEncodingException; +import java.util.Random; + +@Route(path = ConfigConstants.PATH_PLANET_BALL) +public class PlanetBallActivity extends BaseActivity { + @Override + protected int getLayoutId() { + return R.layout.activity_planet_ball; + } + + @Override + protected void init(Bundle savedInstanceState) { + PlanetBallView planetBall = findViewById(R.id.planet_ball); + planetBall.setAdapter(new PlanetAdapter() { + @Override + public int getCount() { + return 50; + } + + @Override + public View getView(Context context, int position, ViewGroup parent) { + PlanetView planetView = new PlanetView(context); + planetView.setSign(getRandomNick()); + + int starColor = position % 2 == 0 ? PlanetView.COLOR_FEMALE : PlanetView.COLOR_MALE; + boolean hasShadow = false; + + String str = ""; + if (position % 12 == 0) { + str = "最活跃"; + starColor = PlanetView.COLOR_MOST_ACTIVE; + } else if (position % 20 == 0) { + str = "最匹配"; + starColor = PlanetView.COLOR_BEST_MATCH; + } else if (position % 33 == 0) { + str = "最新人"; + starColor = PlanetView.COLOR_MOST_NEW; + } else if (position % 18 == 0) { + hasShadow = true; + str = "最闪耀"; + } else { + str = "描述"; + } + planetView.setStarColor(starColor); + planetView.setHasShadow(hasShadow); + planetView.setMatch(position * 2 + "%", str); + if (hasShadow) { + planetView.setMatchColor(starColor); + } else { + planetView.setMatchColor(PlanetView.COLOR_MOST_ACTIVE); + } + int starWidth = ConvertUtils.dp2px(50.0f); + int starHeight = ConvertUtils.dp2px(85.0f); + int starPaddingTop = ConvertUtils.dp2px(20.0f); + ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams(starWidth, starHeight); + planetView.setPadding(0, starPaddingTop, 0, 0); + planetView.setLayoutParams(layoutParams); + return planetView; + } + + @Override + public Object getItem(int position) { + return null; + } + + @Override + public int getPopularity(int position) { + return position % 10; + } + + @Override + public void onThemeColorChanged(View view, int themeColor) { + + } + }); + } + + /** + * 获取随机昵称 + * + * @return 随机昵称 + */ + private String getRandomNick() { + Random random = new Random(); + int len = random.nextInt(12) + 1; + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < len; i++) { + builder.append(getRandomSingleCharacter()); + } + return builder.toString(); + } + + /** + * 获取随机单个汉字 + * + * @return 随机单个汉字 + */ + private String getRandomSingleCharacter() { + String str = ""; + int heightPos; + int lowPos; + Random rd = new Random(); + heightPos = 176 + Math.abs(rd.nextInt(39)); + lowPos = 161 + Math.abs(rd.nextInt(93)); + byte[] bt = new byte[2]; + bt[0] = Integer.valueOf(heightPos).byteValue(); + bt[1] = Integer.valueOf(lowPos).byteValue(); + try { + str = new String(bt, "GBK"); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + return str; + } + +} diff --git a/main/src/main/res/layout/activity_planet_ball.xml b/main/src/main/res/layout/activity_planet_ball.xml new file mode 100644 index 0000000..cbc46de --- /dev/null +++ b/main/src/main/res/layout/activity_planet_ball.xml @@ -0,0 +1,24 @@ + + + + + + \ No newline at end of file diff --git a/main/src/main/res/layout/fragment_home.xml b/main/src/main/res/layout/fragment_home.xml index 8ada212..562f7c6 100644 --- a/main/src/main/res/layout/fragment_home.xml +++ b/main/src/main/res/layout/fragment_home.xml @@ -43,4 +43,28 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> +