国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統(tǒng) > Android > 正文

Android仿支付寶信用分儀表盤控件

2019-11-09 17:36:51
字體:
供稿:網(wǎng)友

無意間打開Github,發(fā)現(xiàn)自己的一個自定義控件項目竟然神奇的被Star了,真的是相當(dāng)驚喜,畢竟這是自己從事代碼工作以來收獲的第一個Star,于是才有了以下這篇博客。花點時間將代碼整理了一下,也配上了一張效果圖,粗糙地完成了以下這篇博客,作為一個筆記進(jìn)行總結(jié)整理,同時提供了一個分享途徑。好了,話不多說,先上效果圖:

示例圖片

以下是主要構(gòu)造

public DashBoardPRogressView(Context context, AttributeSet attrs) { super(context, attrs); initPaint(); RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); dashBoardView = new DashBoardView(context, attrs); addView(dashBoardView, layoutParams); pointView = new PointView(context, attrs); addView(pointView, layoutParams); scoreTextView = new ScoreTextView(context, attrs); addView(scoreTextView, layoutParams); }

通過以上構(gòu)造方法,可以看到該控件主要由三個View組成,分別命名為:

DashBoardView(外邊框和信用等級顯示,刷新頻率最低,只在值滿足條件的時候刷新) PointView(進(jìn)度顯示的點,一直在刷新) ScoreTextView(信用分?jǐn)?shù),一直在刷新)

由于刷新頻率不同,同時根據(jù)具體繪制的內(nèi)容,認(rèn)為分為三個部分還是較合理的,如果放在一個View中進(jìn)行onDraw()繪制,會導(dǎo)致大量不必要的部分重復(fù)繪制。接下來拆分為四個部分進(jìn)行講解,包含以上三個View和一個對外提供的刷新的方法。

1.PointView(容易理解的View)

說其最容易理解是因為它僅僅是一個小圓點圍著表盤的中心點不斷轉(zhuǎn)動到預(yù)設(shè)的值,實現(xiàn)原理就是不斷改變繪制角度后調(diào)用onDraw()方法刷新,見關(guān)鍵代碼:

private class PointView extends View { public PointView(Context context) {// super(context); this(context, null); } public PointView(Context context, AttributeSet attrs) { super(context, attrs); /* * <p>Causes the Runnable to be added to the message queue. * The runnable will be run on the user interface thread.</p> * */ post(new Runnable() { @Override public void run() { initPaint(); } }); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); //轉(zhuǎn)移畫布使繪制的內(nèi)容在所給區(qū)域的中央 if (306 / 576F > height / width) { //給出的大小可能是不和我們的控件匹配的 canvas.translate(Math.abs(width - reguSizeX) / 2, 0); } else { canvas.translate(0, Math.abs(height - reguSizeY) / 2); } //繪制圓點 canvas.save(); canvas.translate(288 / 576f * reguSizeX, 288 / 306f * reguSizeY); canvas.rotate(rotation); canvas.drawCircle(0, -278 / 576f * reguSizeX, 6 / 306f * reguSizeY, paint); canvas.restore(); } }

以上代碼中有兩個需要注意的地方:

1.由于要適配大小,所以有些操作要放在onMeasure之后,但是又必須在繪制之前,使用

post(new Runnable() { @Override public void run() { initPaint(); } });

方法能夠解決這個問題,在繪制圓點的時候還不能很好的體現(xiàn)這個方法的作用,后面繪制文字的時候,由于文字大小也要做適配,所以必須先獲得文字大小再進(jìn)行繪制,所以將繪制文字的畫筆初始化放在了post中,以設(shè)置適配了整體布局大小之后的文字大小(有點描述不清,希望能夠理解……)

2.刷新點的位置的時候先將畫布中心轉(zhuǎn)移到半圓的中間,然后不斷旋轉(zhuǎn)畫布,再繪制圓,達(dá)到小圓點不斷移動的效果

2.ScoreTextView(最容易理解的View)

這個view的代碼見最后的總代碼,只有一個簡單的繪制文字到畫布中間,用來顯示分?jǐn)?shù),唯一的難點在上面的PointView中已經(jīng)做了解釋,使用post在繪制之前獲取布局大小以用來適配并設(shè)置要設(shè)置的文字大小,同時簡單的使用了一個canvas.clipRect()方法來防止繪制臟布局(經(jīng)過測試好像并沒有什么效果)

3.DashBoardView(相對復(fù)雜的View)

相對復(fù)雜指的是該View相對于前面分析的兩個View來說要復(fù)雜一些,其實控件本身并不復(fù)雜,只是要繪制的內(nèi)容多一點,包括線性邊框,分段邊框,斷點組成的邊框和相關(guān)文字部分,其中相對復(fù)雜的就是那個由點組成的邊框了,其實也是使用了旋轉(zhuǎn)畫布的原理,旋轉(zhuǎn)畫布來簡化具有一定規(guī)則的圖形繪制在這篇博客中有詳細(xì)的例子:

http://blog.csdn.net/aigestudio/article/details/41799811

個人認(rèn)為還有一個比較坑的地方就是根據(jù)原型圖來測量文字所在的地方,將文字繪制到固定的位置,該部分少不了要進(jìn)行位置的適配,這些細(xì)節(jié)工作才是比較花時間的部分。給View對應(yīng)的具體代碼在文章后面給出。

該自定義控件已經(jīng)寫成了一個小項目放在了github上,地址:

https://github.com/shixiuwen/DashBoardProgressView

4.刷新方法:refreshScore(int refreshToScore)

刷新方法的代碼如下:

/** * 模擬刷新小圓點的位置 */ public void refreshScore(final int refreshToScore) { score = 500; rotation = -90; new Thread(new Runnable() { @Override public void run() { try { while (score < refreshToScore) { Thread.sleep(50); rotation += 1; score += 2.222; if (score >= refreshToScore) { //該判斷非常重要,防止每次加2.22超出邊界 score = refreshToScore; } pointView.postInvalidate(); scoreTextView.postInvalidate(); double tempScore = score + 0.5; if (tempScore < 600) { scoreLever = 0; dashBoardView.postInvalidate(); } else if (tempScore >= 600 && tempScore <= 603) { scoreLever = 1; dashBoardView.postInvalidate(); } else if (tempScore >= 700 && tempScore <= 703) { scoreLever = 2; dashBoardView.postInvalidate(); } else if (tempScore >= 800 && tempScore <= 803) { scoreLever = 3; dashBoardView.postInvalidate(); } } } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); }

我們在方法中開啟了一個子線程不斷使用postInvalidate()方法已達(dá)到小圓點移動和分?jǐn)?shù)刷新的效果,通過循環(huán) while (score < refreshToScore) 來控制刷新的時機,當(dāng)分?jǐn)?shù)大于或者等于設(shè)置的分?jǐn)?shù)的時候停止刷新,這其中有一個需要注意的地方,分?jǐn)?shù)和角度是對應(yīng)的,不是1:1的比例增加的,比如我這個地方需要用到的就是1:2.22(根據(jù)實際需求),如下:

rotation += 1; score += 2.222;

也就是圓點每旋轉(zhuǎn)一個角度,要增加兩分,所以可能會出現(xiàn)如果設(shè)置的分?jǐn)?shù)是700,每次加2.22剛好最后一次加到了702.22(只是舉例,不一定是這個)才停止的情況,所以我們添加了以下代碼進(jìn)行約束,這也是在代碼中注明特別重要的原因,不然會導(dǎo)致結(jié)果不準(zhǔn)確。

rotation += 1;score += 2.222;if (score >= refreshToScore) { //該判斷非常重要,防止每次加2.22超出邊界 score = refreshToScore;}

以下是所有代碼:

DashBoardProgressView.java

package com.shixia.dashboardprogressview.view;import android.content.Context;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.graphics.Rect;import android.graphics.RectF;import android.util.AttributeSet;import android.util.Log;import android.view.View;import android.view.ViewGroup;import android.widget.FrameLayout;import android.widget.RelativeLayout;/** * Created by AmosShi on 2016/10/27. * <p> * Description:仿支付寶信用分表盤控件,自動更新進(jìn)度,已做好屏幕適配工作,見Demo * <p> * Email:shixiuwen1991@yeah.net * <p> * 分析: * 3個層級: * 1.無需更新的邊框刻度以及分?jǐn)?shù)文字刻度等 * 2.隨刻度移動而變化的文字部分 * 3.移動更新的刻度點 */public class DashBoardProgressView extends FrameLayout { private Paint paint; private Paint textPaint38; private Paint textPaint20; private Paint textPaint60; private RectF rectF; private RectF rectF2; private float rotation = -90; private double score = 500; private int scoreLever = 0; private PointView pointView; private ScoreTextView scoreTextView; private DashBoardView dashBoardView; private float reguSizeX = 0; //以此為參考縮放控件以得到合適大小 private float reguSizeY = 0; private float width; private float height; public DashBoardProgressView(Context context) {// super(context); this(context, null); } public DashBoardProgressView(Context context, AttributeSet attrs) { super(context, attrs); initPaint(); Log.i("amos", "init"); RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); dashBoardView = new DashBoardView(context, attrs); addView(dashBoardView, layoutParams); pointView = new PointView(context, attrs); addView(pointView, layoutParams); scoreTextView = new ScoreTextView(context, attrs); addView(scoreTextView, layoutParams); } private void initPaint() { paint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG); paint.setStyle(Paint.Style.STROKE); paint.setColor(Color.WHITE); paint.setStrokeCap(Paint.Cap.ROUND); } private void initTextPaint38() { textPaint38 = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG); textPaint38.setStyle(Paint.Style.STROKE); textPaint38.setColor(Color.WHITE); textPaint38.setStrokeCap(Paint.Cap.ROUND); textPaint38.setTextSize(38 / 306f * reguSizeY); } private void initTextPaint20() { textPaint20 = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG); textPaint20.setStyle(Paint.Style.STROKE); textPaint20.setColor(Color.WHITE); textPaint20.setStrokeCap(Paint.Cap.ROUND); textPaint20.setTextSize(20 / 306f * reguSizeY); } private void initTextPaint60() { textPaint60 = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG); textPaint60.setStyle(Paint.Style.STROKE); textPaint60.setColor(Color.WHITE); textPaint60.setStrokeCap(Paint.Cap.ROUND); textPaint60.setTextSize(60 / 306f * reguSizeY); } private void initRectF() { Rect rect = new Rect((int) (10 / 576f * reguSizeX), (int) (10 / 306f * reguSizeY), (int) (566 / 576f * reguSizeX), (int) (566 / 306f * reguSizeY)); rectF = new RectF(rect); Rect rect2 = new Rect((int) (42 / 576f * reguSizeX), (int) (42 / 306f * reguSizeY), (int) (534 / 576f * reguSizeX), (int) (534 / 306f * reguSizeY)); rectF2 = new RectF(rect2); } /** * 模擬刷新小圓點的位置 */ public void refreshScore(final int refreshToScore) { score = 500; rotation = -90; new Thread(new Runnable() { @Override public void run() { try { while (score < refreshToScore) { Thread.sleep(50); rotation += 1; score += 2.222; if (score >= refreshToScore) { //該判斷非常重要,防止每次加2.22超出邊界 score = refreshToScore; } pointView.postInvalidate(); scoreTextView.postInvalidate(); double tempScore = score + 0.5; if (tempScore < 600) { scoreLever = 0; dashBoardView.postInvalidate(); } else if (tempScore >= 600 && tempScore <= 603) { scoreLever = 1; dashBoardView.postInvalidate(); } else if (tempScore >= 700 && tempScore <= 703) { scoreLever = 2; dashBoardView.postInvalidate(); } else if (tempScore >= 800 && tempScore <= 803) { scoreLever = 3; dashBoardView.postInvalidate(); } } } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); } private class DashBoardView extends View { public DashBoardView(Context context) {// super(context); this(context, null); } public DashBoardView(Context context, AttributeSet attrs) { super(context, attrs); /* * <p>Causes the Runnable to be added to the message queue. * The runnable will be run on the user interface thread.</p> * 使用了post后,run()中的代碼會提前加載到message queue,提前于onDraw()方法的 * 執(zhí)行,以初始化一些數(shù)據(jù),有些數(shù)據(jù)數(shù)據(jù)是onMeasure()方法中返回的,不這么做的話無法 * 計算比例以適配大小 * */ post(new Runnable() { @Override public void run() { initPaint(); initTextPaint38(); initTextPaint20(); initRectF(); } }); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); //轉(zhuǎn)移畫布使繪制的內(nèi)容在所給區(qū)域的中央 if (306 / 576F > height / width) { //給出的大小可能是不和我們的控件匹配的 canvas.translate(Math.abs(width - reguSizeX) / 2, 0); } else { canvas.translate(0, Math.abs(height - reguSizeY) / 2); } //繪制文字 canvas.save(); canvas.translate(288 / 576f * reguSizeX, 288 / 306f * reguSizeY); canvas.drawText("700", -15 / 576f * reguSizeX, -168 / 306f * reguSizeY, textPaint20); canvas.restore(); canvas.save(); canvas.translate(288 / 576f * reguSizeX, 288 / 306f * reguSizeY); canvas.rotate(45); canvas.drawText("800", -15 / 576f * reguSizeX, -168 / 306f * reguSizeY, textPaint20); canvas.restore(); canvas.save(); canvas.translate(288 / 576f * reguSizeX, 288 / 306f * reguSizeY); canvas.rotate(-45); canvas.drawText("600", -15 / 576f * reguSizeX, -168 / 306f * reguSizeY, textPaint20); canvas.restore(); canvas.save(); canvas.translate(288 / 576f * reguSizeX, 288 / 306f * reguSizeY); canvas.drawText("500", -184 / 576f * reguSizeX, 8 / 306f * reguSizeY, textPaint20); canvas.restore(); canvas.save(); canvas.translate(288 / 576f * reguSizeX, 288 / 306f * reguSizeY); canvas.drawText("900", 148 / 576f * reguSizeX, 8 / 306f * reguSizeY, textPaint20); canvas.restore(); if (scoreLever == 0) { canvas.drawText("信用較差", 213 / 576f * reguSizeX, 196 / 306f * reguSizeY, textPaint38); } else if (scoreLever == 1) { canvas.drawText("信用一般", 213 / 576f * reguSizeX, 196 / 306f * reguSizeY, textPaint38); } else if (scoreLever == 2) { canvas.drawText("信用較好", 213 / 576f * reguSizeX, 196 / 306f * reguSizeY, textPaint38); } else if (scoreLever == 3) { canvas.drawText("信用極好", 213 / 576f * reguSizeX, 196 / 306f * reguSizeY, textPaint38); } //繪制最外框 paint.setStrokeWidth(8 / 306f * reguSizeY); canvas.drawArc(rectF, 175, 190, false, paint); //繪制內(nèi)邊框1,2,3,4,5,6分段(帶有斷點的內(nèi)邊框) paint.setStrokeCap(Paint.Cap.BUTT); paint.setStrokeWidth(16 / 306f * reguSizeY); canvas.drawArc(rectF2, 175, 4, false, paint); canvas.drawArc(rectF2, 181, 43, false, paint); canvas.drawArc(rectF2, 226, 43, false, paint); canvas.drawArc(rectF2, 271, 43, false, paint); canvas.drawArc(rectF2, 316, 43, false, paint); canvas.drawArc(rectF2, 1, 4, false, paint); //繪制大圓點 paint.setStrokeWidth(6 / 306f * reguSizeY); canvas.save(); canvas.drawLine(288 / 576f * reguSizeX, 96 / 306f * reguSizeY, 288 / 576f * reguSizeX, 76 / 306f * reguSizeY, paint); canvas.restore(); canvas.save(); canvas.translate(288 / 576f * reguSizeX, 288 / 306f * reguSizeY); canvas.rotate(45); canvas.drawLine(0, -192 / 306f * reguSizeY, 0, -212 / 306f * reguSizeY, paint); canvas.restore(); canvas.save(); canvas.translate(288 / 576f * reguSizeX, 288); canvas.rotate(90); canvas.drawLine(0, -192 / 306f * reguSizeY, 0, -212 / 306f * reguSizeY, paint); canvas.restore(); canvas.save(); canvas.translate(288 / 576f * reguSizeX, 288 / 306f * reguSizeY); canvas.rotate(-45); canvas.drawLine(0, -192 / 306f * reguSizeY, 0, -212 / 306f * reguSizeY, paint); canvas.restore(); canvas.save(); canvas.translate(288 / 576f * reguSizeX, 288); canvas.rotate(-90); canvas.drawLine(0, -192 / 306f * reguSizeY, 0, -212 / 306f * reguSizeY, paint); canvas.restore(); //繪制小圓點 paint.setStrokeCap(Paint.Cap.ROUND); for (int i = -19; i < 20; i++) { canvas.save(); canvas.translate(288 / 576f * reguSizeX, 288 / 306f * reguSizeY); canvas.rotate(4.5f * i); canvas.drawLine(0, -192 / 306f * reguSizeY, 0, -202 / 306f * reguSizeY, paint); canvas.restore(); } } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {// super.onMeasure(widthMeasureSpec, heightMeasureSpec);// setMeasuredDimension(576, 306); width = getMeasuredSize(widthMeasureSpec, true); height = getMeasuredSize(heightMeasureSpec, false); setMeasuredDimension((int) width, (int) height); if (306 / 576F > height / width) { //給出的大小可能是不和我們的控件匹配的 reguSizeX = height * 576 / 306; reguSizeY = height; } else { reguSizeX = width; reguSizeY = width * 306 / 576; } } } private class PointView extends View { public PointView(Context context) {// super(context); this(context, null); } public PointView(Context context, AttributeSet attrs) { super(context, attrs); /* * <p>Causes the Runnable to be added to the message queue. * The runnable will be run on the user interface thread.</p> * */ post(new Runnable() { @Override public void run() { initPaint(); } }); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); //轉(zhuǎn)移畫布使繪制的內(nèi)容在所給區(qū)域的中央 if (306 / 576F > height / width) { //給出的大小可能是不和我們的控件匹配的 canvas.translate(Math.abs(width - reguSizeX) / 2, 0); } else { canvas.translate(0, Math.abs(height - reguSizeY) / 2); } //繪制圓點 canvas.save(); canvas.translate(288 / 576f * reguSizeX, 288 / 306f * reguSizeY); canvas.rotate(rotation); canvas.drawCircle(0, -278 / 576f * reguSizeX, 6 / 306f * reguSizeY, paint); canvas.restore(); } } private class ScoreTextView extends View { public ScoreTextView(Context context) {// super(context); this(context, null); } public ScoreTextView(Context context, AttributeSet attrs) { super(context, attrs); /* * <p>Causes the Runnable to be added to the message queue. * The runnable will be run on the user interface thread.</p> * */ post(new Runnable() { @Override public void run() { initTextPaint60(); } }); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); //轉(zhuǎn)移畫布使繪制的內(nèi)容在所給區(qū)域的中央 if (306 / 576F > height / width) { //給出的大小可能是不和我們的控件匹配的 canvas.translate(Math.abs(width - reguSizeX) / 2, 0); } else { canvas.translate(0, Math.abs(height - reguSizeY) / 2); } //優(yōu)化,防止繪制臟布局 canvas.clipRect(232 / 576f * reguSizeX, 280 / 306f * reguSizeY - textPaint60.getTextSize() , 232 / 576f * reguSizeX + textPaint60.measureText(String.valueOf((int) (score + 0.5))), 280 / 306f * reguSizeY); canvas.drawText(String.valueOf((int) (score + 0.5)), 232 / 576f * reguSizeX, 280 / 306f * reguSizeY, textPaint60); } } private int getMeasuredSize(int length, boolean isWidth) { int mode = MeasureSpec.getMode(length); int size = MeasureSpec.getSize(length); int resSize = 0; if (mode == MeasureSpec.EXACTLY) { resSize = size; } else { if (mode == MeasureSpec.AT_MOST) { if (isWidth) { resSize = 576; } else { resSize = 306; } } } return resSize; }}

MainActivity.java

package com.shixia.dashboardprogressview;import android.os.Bundle;import android.support.v7.app.AppCompatActivity;import android.view.View;import android.widget.Button;import com.shixia.dashboardprogressview.view.DashBoardProgressView;public class MainActivity extends AppCompatActivity { private Button btnRefreshTo550; private Button btnRefreshTo650; private DashBoardProgressView wpbView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); wpbView = (DashBoardProgressView) findViewById(R.id.wpb_progress_view); btnRefreshTo550 = (Button) findViewById(R.id.btn_refresh_to550); btnRefreshTo650 = (Button) findViewById(R.id.btn_refresh_to650); wpbView.refreshScore(800); btnRefreshTo550.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { wpbView.refreshScore(550); } }); btnRefreshTo650.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { wpbView.refreshScore(650); } }); }}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?><android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.shixia.dashboardprogressview.MainActivity"> <RelativeLayout android:layout_width="match_parent" android:layout_height="match_parent" android:background="#dcdcdc"> <LinearLayout android:layout_width="1000px" android:layout_height="800px" android:layout_centerInParent="true" android:background="#43c0dc" android:gravity="center"> <com.shixia.dashboardprogressview.view.DashBoardProgressView android:layout_width="580px" android:layout_height="300px" android:layout_centerInParent="true" android:id="@+id/wpb_progress_view"/> </LinearLayout> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/btn_refresh_to550" android:text="refresh To 550" android:layout_alignParentBottom="true" android:layout_centerHorizontal="true" android:layout_marginBottom="100px"/> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/btn_refresh_to650" android:text="refresh To 650" android:layout_alignParentBottom="true" android:layout_centerHorizontal="true" android:layout_marginBottom="300px"/> </RelativeLayout></android.support.constraint.ConstraintLayout>

PS:該自定義控件除了設(shè)置刷新分?jǐn)?shù)外沒有對外提供相關(guān)屬性設(shè)置方法,只想分享一個解決這種問題的思路,真的要使用的話請自行修改,如有任何問題請留言或者郵箱:shixiuwen1991@yeah.net


發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 阿勒泰市| 信阳市| 许昌市| 萨迦县| 麟游县| 阳江市| 苏尼特右旗| 商丘市| 张北县| 宿迁市| 泗洪县| 凌海市| 康平县| 长治市| 大石桥市| 怀柔区| 西吉县| 重庆市| 台山市| 昭通市| 上高县| 万全县| 保康县| 三江| 安化县| 绥中县| 鞍山市| 锡林郭勒盟| 曲麻莱县| 屏山县| 巴林左旗| 清远市| 孟州市| 南投县| 达日县| 清远市| 盈江县| 鄂尔多斯市| 且末县| 容城县| 阿尔山市|