当前位置:   article > 正文

Android 自定义view 入门 案例_android studio自定义view

android studio自定义view

自定义一个圆环进度条:

1.首页Android Studio创建一个项目

2.在项目src/xxx/目录下右键选择创建一个自定义view页面:new->UICompoent->customer view

 3.输入自定义名称,选择开发语言

 4.确定之后,自动生成3个文件一个是:

第一个是逻辑代码:com.lan.lanidemo.customeview.SuperCircleView.class
第二个是布局: src\main\res\layout\sample_super_circle_view.xml
第三个是view需要使用的属性:src\main\res\values\attrs_super_circle_view.xml
      (通过这种方式创建属性,多次创建可能造成一些变量重复定义。)

attrs_super_circle_view.xml源码: 

  1. <resources>
  2. <declare-styleable name="SuperCircleView">
  3. <attr name="exampleString" format="string" />
  4. <attr name="exampleDimension" format="dimension" />
  5. <attr name="exampleColor" format="color" />
  6. <attr name="exampleDrawable" format="color|reference" />
  7. </declare-styleable>
  8. </resources>

其中declare-styeable节点的name属性值一般是你写的view的名字,如这里根据命名默认生成:SuperCirecleView。接下来定义可以在xml中定义的组件属性,在后面我会根据需要增加圆环宽度颜色等。其中format属性指定可接受值的类型,多个类型用“|”分隔。 

 sample_super_circle_view.xml布局源码

  1. <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2. xmlns:app="http://schemas.android.com/apk/res-auto"
  3. android:layout_width="match_parent"
  4. android:layout_height="match_parent">
  5. <com.lan.lanidemo.customeview.SuperCircleView
  6. style="@style/Widget.Theme.LaniDemo.SuperCircleView"
  7. android:layout_width="300dp"
  8. android:layout_height="300dp"
  9. android:paddingLeft="20dp"
  10. android:paddingBottom="40dp"
  11. app:exampleDimension="24sp"
  12. app:exampleDrawable="@android:drawable/ic_menu_add"
  13. app:exampleString="Hello, SuperCircleView" />
  14. </FrameLayout>

SuperCircleView.class 

  1. package com.lan.lanidemo.customeview;
  2. import android.content.Context;
  3. import android.content.res.TypedArray;
  4. import android.graphics.Canvas;
  5. import android.graphics.Color;
  6. import android.graphics.Paint;
  7. import android.graphics.drawable.Drawable;
  8. import android.text.TextPaint;
  9. import android.util.AttributeSet;
  10. import android.view.View;
  11. import com.lan.lanidemo.R;
  12. /**
  13. * TODO: document your custom view class.
  14. */
  15. public class SuperCircleView extends View {
  16. private String mExampleString; // TODO: use a default from R.string...
  17. private int mExampleColor = Color.RED; // TODO: use a default from R.color...
  18. private float mExampleDimension = 0; // TODO: use a default from R.dimen...
  19. private Drawable mExampleDrawable;
  20. private TextPaint mTextPaint;
  21. private float mTextWidth;
  22. private float mTextHeight;
  23. public SuperCircleView(Context context) {
  24. super( context );
  25. init( null, 0 );
  26. }
  27. public SuperCircleView(Context context, AttributeSet attrs) {
  28. super( context, attrs );
  29. init( attrs, 0 );
  30. }
  31. public SuperCircleView(Context context, AttributeSet attrs, int defStyle) {
  32. super( context, attrs, defStyle );
  33. init( attrs, defStyle );
  34. }
  35. private void init(AttributeSet attrs, int defStyle) {
  36. // Load attributes
  37. final TypedArray a = getContext().obtainStyledAttributes(
  38. attrs, R.styleable.SuperCircleView, defStyle, 0 );
  39. mExampleString = a.getString(
  40. R.styleable.SuperCircleView_exampleString );
  41. mExampleColor = a.getColor(
  42. R.styleable.SuperCircleView_exampleColor,
  43. mExampleColor );
  44. // Use getDimensionPixelSize or getDimensionPixelOffset when dealing with
  45. // values that should fall on pixel boundaries.
  46. mExampleDimension = a.getDimension(
  47. R.styleable.SuperCircleView_exampleDimension,
  48. mExampleDimension );
  49. if (a.hasValue( R.styleable.SuperCircleView_exampleDrawable )) {
  50. mExampleDrawable = a.getDrawable(
  51. R.styleable.SuperCircleView_exampleDrawable );
  52. mExampleDrawable.setCallback( this );
  53. }
  54. a.recycle();
  55. // Set up a default TextPaint object
  56. mTextPaint = new TextPaint();
  57. mTextPaint.setFlags( Paint.ANTI_ALIAS_FLAG );
  58. mTextPaint.setTextAlign( Paint.Align.LEFT );
  59. // Update TextPaint and text measurements from attributes
  60. invalidateTextPaintAndMeasurements();
  61. }
  62. private void invalidateTextPaintAndMeasurements() {
  63. mTextPaint.setTextSize( mExampleDimension );
  64. mTextPaint.setColor( mExampleColor );
  65. mTextWidth = mTextPaint.measureText( mExampleString );
  66. Paint.FontMetrics fontMetrics = mTextPaint.getFontMetrics();
  67. mTextHeight = fontMetrics.bottom;
  68. }
  69. @Override
  70. protected void onDraw(Canvas canvas) {
  71. super.onDraw( canvas );
  72. // TODO: consider storing these as member variables to reduce
  73. // allocations per draw cycle.
  74. int paddingLeft = getPaddingLeft();
  75. int paddingTop = getPaddingTop();
  76. int paddingRight = getPaddingRight();
  77. int paddingBottom = getPaddingBottom();
  78. int contentWidth = getWidth() - paddingLeft - paddingRight;
  79. int contentHeight = getHeight() - paddingTop - paddingBottom;
  80. // Draw the text.
  81. canvas.drawText( mExampleString,
  82. paddingLeft + (contentWidth - mTextWidth) / 2,
  83. paddingTop + (contentHeight + mTextHeight) / 2,
  84. mTextPaint );
  85. // Draw the example drawable on top of the text.
  86. if (mExampleDrawable != null) {
  87. mExampleDrawable.setBounds( paddingLeft, paddingTop,
  88. paddingLeft + contentWidth, paddingTop + contentHeight );
  89. mExampleDrawable.draw( canvas );
  90. }
  91. }
  92. /**
  93. * Gets the example string attribute value.
  94. *
  95. * @return The example string attribute value.
  96. */
  97. public String getExampleString() {
  98. return mExampleString;
  99. }
  100. /**
  101. * Sets the view"s example string attribute value. In the example view, this string
  102. * is the text to draw.
  103. *
  104. * @param exampleString The example string attribute value to use.
  105. */
  106. public void setExampleString(String exampleString) {
  107. mExampleString = exampleString;
  108. invalidateTextPaintAndMeasurements();
  109. }
  110. /**
  111. * Gets the example color attribute value.
  112. *
  113. * @return The example color attribute value.
  114. */
  115. public int getExampleColor() {
  116. return mExampleColor;
  117. }
  118. /**
  119. * Sets the view"s example color attribute value. In the example view, this color
  120. * is the font color.
  121. *
  122. * @param exampleColor The example color attribute value to use.
  123. */
  124. public void setExampleColor(int exampleColor) {
  125. mExampleColor = exampleColor;
  126. invalidateTextPaintAndMeasurements();
  127. }
  128. /**
  129. * Gets the example dimension attribute value.
  130. *
  131. * @return The example dimension attribute value.
  132. */
  133. public float getExampleDimension() {
  134. return mExampleDimension;
  135. }
  136. /**
  137. * Sets the view"s example dimension attribute value. In the example view, this dimension
  138. * is the font size.
  139. *
  140. * @param exampleDimension The example dimension attribute value to use.
  141. */
  142. public void setExampleDimension(float exampleDimension) {
  143. mExampleDimension = exampleDimension;
  144. invalidateTextPaintAndMeasurements();
  145. }
  146. /**
  147. * Gets the example drawable attribute value.
  148. *
  149. * @return The example drawable attribute value.
  150. */
  151. public Drawable getExampleDrawable() {
  152. return mExampleDrawable;
  153. }
  154. /**
  155. * Sets the view"s example drawable attribute value. In the example view, this drawable is
  156. * drawn above the text.
  157. *
  158. * @param exampleDrawable The example drawable attribute value to use.
  159. */
  160. public void setExampleDrawable(Drawable exampleDrawable) {
  161. mExampleDrawable = exampleDrawable;
  162. }
  163. }

5.在这里我是做一圆环进度条:如本文开始图片效果,更新代码如下。

 6. 更新自定义属性

attrs_super_circle_view.xml

  1. <resources>
  2. <declare-styleable name="SuperCircleView">
  3. <attr name="exampleString" format="string" />
  4. <attr name="exampleDimension" format="dimension" />
  5. <attr name="exampleColor" format="color" />
  6. <attr name="exampleDrawable" format="color|reference" />
  7. <!-- 圆的半径 -->
  8. <attr name="min_circle_radio" format="integer" />
  9. <!-- 圆环的宽度 -->
  10. <attr name="ring_width" format="float" />
  11. <!-- 内圆的颜色 -->
  12. <attr name="circle_color" format="color" />
  13. <!-- 外圆的颜色 -->
  14. <attr name="max_circle_color" format="color" />
  15. <!-- 圆环的默认颜色 -->
  16. <attr name="ring_normal_color" format="color" />
  17. <!-- 圆环要显示的彩色的区域(随着数值的改变,显示不同大小的彩色区域)-->
  18. <attr name="ring_color_select" format="integer" />
  19. <!-- 绘制内容的数值 -->
  20. <attr name="maxValue" format="integer" />
  21. <attr name="value" format="integer" />
  22. <attr name="ring_radius" format="float" />
  23. </declare-styleable>
  24. </resources>

7.更新自定义view控制代码:

SuperCircleView.class

  1. package com.lan.lanidemo.customeview;
  2. import android.animation.ValueAnimator;
  3. import android.content.Context;
  4. import android.content.res.TypedArray;
  5. import android.graphics.Canvas;
  6. import android.graphics.Color;
  7. import android.graphics.Paint;
  8. import android.graphics.RectF;
  9. import android.graphics.SweepGradient;
  10. import android.graphics.drawable.Drawable;
  11. import android.text.TextPaint;
  12. import android.util.AttributeSet;
  13. import android.util.Log;
  14. import android.view.View;
  15. import android.widget.TextView;
  16. import com.lan.lanidemo.R;
  17. import static android.content.ContentValues.TAG;
  18. /**
  19. * TODO: document your custom view class.
  20. */
  21. public class SuperCircleView extends View {
  22. private String mExampleString; // TODO: use a default from R.string...
  23. private int mExampleColor = Color.RED; // TODO: use a default from R.color...
  24. private float mExampleDimension = 0; // TODO: use a default from R.dimen...
  25. private Drawable mExampleDrawable;
  26. private TextPaint mTextPaint;
  27. private float mTextWidth;
  28. private float mTextHeight;
  29. private ValueAnimator valueAnimator;
  30. private int mViewCenterX; //view宽的中心点(可以暂时理解为圆心)
  31. private int mViewCenterY; //view高的中心点(可以暂时理解为圆心)
  32. private int mMinRadio; //最里面白色圆的半径
  33. private float mRingWidth; //圆环的宽度
  34. private int mMinCircleColor; //最里面圆的颜色
  35. private int mRingNormalColor; //默认圆环的颜色
  36. private Paint mPaint;
  37. private int color[] = new int[3]; //渐变颜色
  38. private RectF mRectF; //圆环的矩形区域
  39. private int mSelectRing = 0; //要显示的彩色区域(岁数值变化)
  40. float ringRadius;
  41. private int mMaxValue;
  42. public SuperCircleView(Context context) {
  43. super( context );
  44. Log.d( TAG, "SuperCircleView: >>构造" );
  45. init( null, 0 );
  46. }
  47. public SuperCircleView(Context context, AttributeSet attrs) {
  48. super( context, attrs );
  49. Log.d( TAG, "SuperCircleView: >>构造2" );
  50. init( attrs, 0 );
  51. }
  52. public SuperCircleView(Context context, AttributeSet attrs, int defStyle) {
  53. super( context, attrs, defStyle );
  54. init( attrs, defStyle );
  55. }
  56. private void init(AttributeSet attrs, int defStyle) {
  57. // Load attributes
  58. TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.SuperCircleView);
  59. //最里面白色圆的半径
  60. mMinRadio = a.getInteger(R.styleable.SuperCircleView_min_circle_radio, 300);
  61. //圆环宽度
  62. mRingWidth = a.getFloat(R.styleable.SuperCircleView_ring_width, 40);
  63. //最里面的圆的颜色(绿色)
  64. mMinCircleColor = a.getColor(R.styleable.SuperCircleView_circle_color,
  65. getContext().getResources().getColor(R.color.green));
  66. //圆环的默认颜色(圆环占据的是里面的圆的空间)
  67. mRingNormalColor = a.getColor(R.styleable.SuperCircleView_ring_normal_color,
  68. getContext().getResources().getColor(R.color.gray));
  69. //圆环要显示的彩色的区域
  70. mSelectRing = a.getInt(R.styleable.SuperCircleView_ring_color_select, 0);
  71. mMaxValue = a.getInt(R.styleable.SuperCircleView_maxValue, 100);
  72. ringRadius = a.getDimension( R.styleable.SuperCircleView_ring_radius, dp2px(100) );
  73. a.recycle();
  74. //抗锯齿画笔
  75. mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
  76. //防止边缘锯齿
  77. mPaint.setAntiAlias(true);
  78. //需要重写onDraw就得调用此
  79. this.setWillNotDraw(false);
  80. //圆环渐变的颜色
  81. color[0] = Color.parseColor("#FFD300");
  82. color[1] = Color.parseColor("#FF0084");
  83. color[2] = Color.parseColor("#16FF00");
  84. }
  85. public int dp2px(float dpValue) {
  86. final float scale = Resources.getSystem().getDisplayMetrics().density;
  87. return (int) (dpValue * scale + 0.5f);
  88. }
  89. @Override
  90. protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
  91. super.onLayout(changed, left, top, right, bottom);
  92. //view的宽和高,相对于父布局(用于确定圆心)
  93. int viewWidth = getMeasuredWidth();
  94. int viewHeight = getMeasuredHeight();
  95. mViewCenterX = viewWidth / 2;
  96. mViewCenterY = viewHeight / 2;
  97. mRectF = new RectF(
  98. mViewCenterX - ringRadius,
  99. mViewCenterY - ringRadius,
  100. mViewCenterX + ringRadius,
  101. mViewCenterY + ringRadius
  102. );
  103. }
  104. private void invalidateTextPaintAndMeasurements() {
  105. mTextPaint.setTextSize( mExampleDimension );
  106. mTextPaint.setColor( mExampleColor );
  107. mTextWidth = mTextPaint.measureText( mExampleString );
  108. Paint.FontMetrics fontMetrics = mTextPaint.getFontMetrics();
  109. mTextHeight = fontMetrics.bottom;
  110. }
  111. @Override
  112. protected void onDraw(Canvas canvas) {
  113. super.onDraw( canvas );
  114. mPaint.setColor(mMinCircleColor);
  115. canvas.drawCircle(mViewCenterX, mViewCenterY, mMinRadio, mPaint);
  116. //画默认圆环
  117. drawNormalRing(canvas);
  118. //画彩色圆环
  119. drawColorRing(canvas);
  120. }
  121. /**
  122. * 画默认圆环
  123. *
  124. * @param canvas
  125. */
  126. private void drawNormalRing(Canvas canvas) {
  127. Paint ringNormalPaint = new Paint(mPaint);
  128. ringNormalPaint.setStyle(Paint.Style.STROKE);
  129. ringNormalPaint.setStrokeWidth(mRingWidth);
  130. ringNormalPaint.setColor(mRingNormalColor);//圆环默认颜色为灰色
  131. canvas.drawArc(mRectF, 360, 360, false, ringNormalPaint);
  132. }
  133. /**
  134. * 画彩色圆环
  135. *
  136. * @param canvas
  137. */
  138. private void drawColorRing(Canvas canvas) {
  139. Paint ringColorPaint = new Paint(mPaint);
  140. ringColorPaint.setStyle(Paint.Style.STROKE);
  141. ringColorPaint.setStrokeWidth(mRingWidth);
  142. ringColorPaint.setShader(new SweepGradient(mViewCenterX, mViewCenterX, color, null));
  143. //逆时针旋转90度
  144. canvas.rotate(-90, mViewCenterX, mViewCenterY);
  145. canvas.drawArc(mRectF, 360, mSelectRing, false, ringColorPaint);
  146. ringColorPaint.setShader(null);
  147. }
  148. //***************************************用于更新圆环表示的数值*****************************************************
  149. /**
  150. * 设置当前值
  151. *
  152. * @param value
  153. */
  154. public void setValue(int value,TextView textView) {
  155. if (value > mMaxValue) {
  156. value = mMaxValue;
  157. }
  158. int start = 0;
  159. int end = value;
  160. startAnimator(start, end, 2000,textView);
  161. }
  162. private void startAnimator(int start, int end, long animTime, final TextView textView) {
  163. valueAnimator = ValueAnimator.ofInt(start, end);
  164. valueAnimator.setDuration(animTime);
  165. valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
  166. @Override
  167. public void onAnimationUpdate(ValueAnimator animation) {
  168. Log.i(TAG, "onAnimationUpdate: animation.getAnimatedValue()::"+animation.getAnimatedValue());
  169. int i = Integer.valueOf(String.valueOf(animation.getAnimatedValue()));
  170. textView.setText(i + "");
  171. //每个单位长度占多少度
  172. mSelectRing=(int) (360 * (i / 100f));
  173. Log.i(TAG, "onAnimationUpdate: mSelectRing::"+mSelectRing);
  174. invalidate();
  175. }
  176. });
  177. valueAnimator.start();
  178. }
  179. /**
  180. * Gets the example string attribute value.
  181. *
  182. * @return The example string attribute value.
  183. */
  184. public String getExampleString() {
  185. return mExampleString;
  186. }
  187. /**
  188. * Sets the view"s example string attribute value. In the example view, this string
  189. * is the text to draw.
  190. *
  191. * @param exampleString The example string attribute value to use.
  192. */
  193. public void setExampleString(String exampleString) {
  194. mExampleString = exampleString;
  195. invalidateTextPaintAndMeasurements();
  196. }
  197. /**
  198. * Gets the example color attribute value.
  199. *
  200. * @return The example color attribute value.
  201. */
  202. public int getExampleColor() {
  203. return mExampleColor;
  204. }
  205. /**
  206. * Sets the view"s example color attribute value. In the example view, this color
  207. * is the font color.
  208. *
  209. * @param exampleColor The example color attribute value to use.
  210. */
  211. public void setExampleColor(int exampleColor) {
  212. mExampleColor = exampleColor;
  213. invalidateTextPaintAndMeasurements();
  214. }
  215. /**
  216. * Gets the example dimension attribute value.
  217. *
  218. * @return The example dimension attribute value.
  219. */
  220. public float getExampleDimension() {
  221. return mExampleDimension;
  222. }
  223. /**
  224. * Sets the view"s example dimension attribute value. In the example view, this dimension
  225. * is the font size.
  226. *
  227. * @param exampleDimension The example dimension attribute value to use.
  228. */
  229. public void setExampleDimension(float exampleDimension) {
  230. mExampleDimension = exampleDimension;
  231. invalidateTextPaintAndMeasurements();
  232. }
  233. /**
  234. * Gets the example drawable attribute value.
  235. *
  236. * @return The example drawable attribute value.
  237. */
  238. public Drawable getExampleDrawable() {
  239. return mExampleDrawable;
  240. }
  241. /**
  242. * Sets the view"s example drawable attribute value. In the example view, this drawable is
  243. * drawn above the text.
  244. *
  245. * @param exampleDrawable The example drawable attribute value to use.
  246. */
  247. public void setExampleDrawable(Drawable exampleDrawable) {
  248. mExampleDrawable = exampleDrawable;
  249. }
  250. }

8.更新自定义布局:sample_super_circle_view.xml

  1. <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2. xmlns:app="http://schemas.android.com/apk/res-auto"
  3. android:layout_width="match_parent"
  4. android:layout_height="match_parent">
  5. <com.lan.lanidemo.customeview.SuperCircleView
  6. style="@style/Widget.Theme.LaniDemo.MyView"
  7. android:layout_width="300dp"
  8. android:layout_height="300dp"
  9. android:paddingLeft="20dp"
  10. android:paddingBottom="40dp"
  11. app:exampleDimension="24sp"
  12. app:exampleDrawable="@android:drawable/ic_menu_add"
  13. app:exampleString="Hello, SuperCircleView" />
  14. </FrameLayout>

9.最后在新建一个页面:ThreeActivity, 并在AndroidManifest.xml,设置为启动页面。创建完成后,在xml添加自定义view:activity_three.xml

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. xmlns:app="http://schemas.android.com/apk/res-auto"
  4. android:layout_width="match_parent"
  5. android:layout_height="match_parent"
  6. android:orientation="vertical">
  7. <TextView
  8. android:id="@+id/tv_three"
  9. android:layout_width="match_parent"
  10. android:layout_height="wrap_content"
  11. android:text="This is Three page"
  12. android:textSize="30sp" />
  13. <FrameLayout
  14. android:layout_width="300dp"
  15. android:layout_height="300dp"
  16. android:layout_gravity="center">
  17. <com.lan.lanidemo.customeview.SuperCircleView
  18. android:id="@+id/superview"
  19. android:layout_width="wrap_content"
  20. android:layout_height="wrap_content"
  21. android:layout_gravity="center"
  22. app:maxValue="100"
  23. app:ring_width="60"
  24. app:value="20" />
  25. <TextView
  26. android:layout_width="wrap_content"
  27. android:layout_height="wrap_content"
  28. android:layout_gravity="center"
  29. android:layout_marginBottom="60dp"
  30. android:text="信息完成度"
  31. android:textColor="#CFD5DE"
  32. android:textSize="18sp" />
  33. <LinearLayout
  34. android:layout_width="wrap_content"
  35. android:layout_height="wrap_content"
  36. android:layout_gravity="center"
  37. android:layout_marginTop="10dp"
  38. android:orientation="horizontal">
  39. <TextView
  40. android:id="@+id/tv"
  41. android:layout_width="wrap_content"
  42. android:layout_height="wrap_content"
  43. android:text="0"
  44. android:textColor="#506946"
  45. android:textSize="80sp" />
  46. <TextView
  47. android:layout_width="wrap_content"
  48. android:layout_height="wrap_content"
  49. android:text="%"
  50. android:textSize="28sp" />
  51. </LinearLayout>
  52. </FrameLayout>
  53. </LinearLayout>

10. ThreeActivity.class 在启动页设置百分比。 同时增加一个点击事件,点击view可以随机变化百分比值。

  1. package com.lan.lanidemo;
  2. import android.os.Bundle;
  3. import android.util.Log;
  4. import android.view.View;
  5. import android.widget.TextView;
  6. import com.lan.lanidemo.customeview.SuperCircleView;
  7. import java.util.Random;
  8. import androidx.appcompat.app.AppCompatActivity;
  9. public class ThreeActivity extends AppCompatActivity {
  10. private static final String TAG = "ThreeActivity";
  11. private TextView mTextView;
  12. SuperCircleView mSuperCircleView;
  13. @Override
  14. protected void onCreate(Bundle savedInstanceState) {
  15. super.onCreate( savedInstanceState );
  16. setContentView( R.layout.activity_three );
  17. initView();
  18. }
  19. private void initView() {
  20. mTextView = (TextView) findViewById( R.id.tv );
  21. mSuperCircleView = findViewById(R.id.superview);
  22. mSuperCircleView.setValue(70, mTextView);
  23. mSuperCircleView.setOnClickListener(new View.OnClickListener() {
  24. @Override
  25. public void onClick(View v) {
  26. //随机设定圆环大小
  27. int i = new Random().nextInt(100) + 1;
  28. Log.i(TAG, "onClick: i::" + i);
  29. mSuperCircleView.setValue(i, mTextView);
  30. }
  31. });
  32. }
  33. }

11.最后运行,点击之后进度更新。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Gausst松鼠会/article/detail/673440
推荐阅读
相关标签
  

闽ICP备14008679号