91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

android中怎么實現一個阻尼下拉刷新列表

發布時間:2021-06-28 18:03:43 來源:億速云 閱讀:336 作者:Leah 欄目:移動開發

android中怎么實現一個阻尼下拉刷新列表,相信很多沒有經驗的人對此束手無策,為此本文總結了問題出現的原因和解決方法,通過這篇文章希望你能解決這個問題。

1. 如何調用

雖然效果圖看起來樣子不太好看,主要是因為那個藍色的背景對不對,沒關系,這只是一個背景而已,在了解了我們這個下拉刷新列表的實現之后,你就可以很輕松地修改這個背景,從而實現你想要的UI效果!話不多說,下面我們先來講講這個下拉刷新列表是如何使用的,這也是我們編寫代碼所要實現的目標。

final PullToRefreshListView eListView = (PullToRefreshListView) rootView.findViewById(R.id.profile_listView);
    eListView.setOnLoadCallBack(new PullToRefreshListView.OnLoadCallBack() {
      @Override
      public int whereToLoad() {
        return PullToRefreshListView.DEFAULT_WHERE_TO_LOAD;
      }
      @Override
      public void onLoad() {
        eListView.postDelayed(new Runnable() {
          @Override
          public void run() {
            eListView.setLoadingFinish();
          }
        }, 5000);
      }
      @Override
      public void cancelLoad() {
      }
      @Override
      public Drawable refreshDrawable() {
        return new ColorDrawable(Color.CYAN);
      }
    });
    eListView.setAdapter(new BaseAdapter() {
      @Override
      public int getCount() {
        return 30;
      }
      @Override
      public Object getItem(int position) {
        return null;
      }
      @Override
      public long getItemId(int position) {
        return 0;
      }
      @Override
      public View getView(int position, View convertView, ViewGroup parent) {
        TextView tv;
        if (convertView == null) {
          tv = new TextView(getActivity());
          tv.setGravity(Gravity.CENTER_VERTICAL);
          tv.setHeight(200);
          tv.setBackgroundColor(Color.WHITE);
        } else {
          tv = (TextView) convertView;
        }
        tv.setText(position+"");
        return tv;
      }
    });

在上述代碼中,我們可以看到PullToRefreshListView的使用在adapter上跟ListView是一樣的,這個當然,因為我們實現下拉刷新功能并不需要修改數據適配器。我們也看到,PullToRefreshListView的實例需要設置一個OnLoadCallBack回調,該回調需要實現4個方法,包括:

  /**
   * 下拉刷新的回調
   */
  public interface OnLoadCallBack {
    /**
     * 下拉結束后將listView定位到哪個位置等待刷新完成
     * @return listView的定位y坐標值,in dp
     */
    int whereToLoad();
    /**
     * 下拉結束后進行刷新的回調
     */
    void onLoad();
    /**
     * 取消刷新
     */
    void cancelLoad();
    /**
     * 下拉刷新的背景
     * @return 背景drawable
     */
    Drawable refreshDrawable();
  }

whereToLoad方法告知PullToRefreshListView對象下拉刷新時停留在哪個位置,具體點說,也就是上述第二章效果圖中藍色背景的高度。onLoad方法是下拉刷新的回調,調用者可以在這里實現刷新動作。cancelLoad方法是取消刷新動作的回調,調用者需要在這里將刷新動作取消。

根據上述方法,我們可以猜測,在onLoad方法中執行的應該是一個線程或者AsyncTask,而在cancelLoad方法中要做的就是將這個線程或者AsyncTask取消掉。最后還有一個refreshDrawable方法,這個方法是為修改listView的背景而提供給調用者的,調用者可以返回任意一個喜歡的背景Drawable。

知道如何調用以后,我們就要一步一步地實現這個PullToRefreshListView了。

2. 在dispatchDraw中重畫子View實現下拉視覺

PullToRefreshListView實現的關鍵在于重畫該listVIew的子View。重畫ViewGroup的子View一般是在dispatchDraw方法中實現的。因此,我們的PullToRefreshListView繼承自ListView類,重載其dispatchDraw方法。

  @Override
  protected void dispatchDraw(Canvas canvas) {
    super.dispatchDraw(canvas);
    if (distanceY > 0) {
      if (refreshDrawable == null) {
        refreshDrawable = onLoadCallBack.refreshDrawable();
      }
      if (refreshDrawable == null) {
        canvas.drawColor(Color.GRAY);
      } else {
        int left = getPaddingLeft();
        int top = getPaddingTop();
        refreshDrawable.setBounds(left, top, getWidth()+left, getHeight()+top);
        refreshDrawable.draw(canvas);
      }
      canvas.save();
      canvas.translate(getPaddingLeft(), getPaddingTop() + distanceY);
      for (int i=0;i<getChildCount();i++) {
        View child = getChildAt(i);
        drawChild(canvas, child, getDrawingTime());
      }
      canvas.restore();
    }
  }

重畫子View的關鍵在于這一句代碼:

canvas.translate(getPaddingLeft(), getPaddingTop() + distanceY);

在重畫子View之前,我們需要先將canvas向上移動distanceY距離。這是為什么呢?我們先來看看在canvas畫子View的方法

drawChild方法的文檔是怎么說的。

protected boolean drawChild (Canvas canvas, View child, long drawingTime)

Added in API level 1 Draw one child of this View Group. This method is responsible for getting the canvas in the right state. This includes clipping, translating so that the child's scrolled origin is at 0, 0, and applying any animation transformations.

Parameters canvas The canvas on which to draw the child child Who to draw drawingTime The time at which draw is occurring Returns True if an invalidate() was issued

我來翻譯一下,drawChild方法可以畫出這個View Group的一個子View。該方法需要使canvas處于一個正確的狀態,該狀態就

是通過對canvas進行clip裁剪,translate評議操作等以使得該子View位于canvas的(0,0)位置。

什么意思呢?簡單來說就是,drawChild方法會將child view畫在canvas的(0,0)位置,因此為了使得該child view位于

canvas的正確位置,我們需要在重畫之前對canvas進行裁剪平移等操作。舉個例子,有一個canvas和一個child view,本來

child view要畫在(0,0)位置上,于是呈現在我們眼前的child view就是位于canvas的頂部,但是如果在畫之前我們將

canvas向上移動100個像素單位,然后再將child view畫在(0,0)位置上,那么呈現在我們眼前的child view的位置將會是

位于canvas的(0,100)位置上。

根據以上分析,我們可以知道,重畫子View的原理就是:

當PullToRefreshListView已經滾動到頂部的時候,通過監控滑動手勢來計算distanceY,從而確定要將canvas向上移動多少再重畫子View,就可以實現PullToRefreshListView跟隨滑動手勢進行下拉的功能了。

3. 計算下拉距離

實現了重畫以后,我們需要做的就是如何計算distanceY。我們的初步想法是,根據滑動的距離來計算,考慮到我們要實現阻尼效果,即隨著滑動距離的變長,PullToRefreshListView的下拉距離會越來越短。在PullToRefreshListView實現中,我使用指數函數來實現這一阻尼效果,具體計算如下:

distanceY = ev.getY() - pullStartY; 
distanceY = (float) (Math.exp(-ev.getY() / pullStartY / 40) * distanceY);

我們知道負指數是加速度隨距離變小的單調遞增函數,我使用手指滑動距離計算負指數作為PullToRefreshListView的滑動距離的參考標準,便可以實現有阻尼下拉效果。

4. 監控手勢判斷ListView是否進入下拉狀態并更新distanceY

更進一步,我們要實現的就是對手勢的監控,在PullToRefreshListView中,我們在onTouchEvent方法中進行處理。

@Override 
public boolean onTouchEvent(MotionEvent ev) { 
  if (lastAction == -1 && ev.getActionMasked() == MotionEvent.ACTION_DOWN) { 
    // 按下的時候 
    lastAction = MotionEvent.ACTION_DOWN; 
    cancelAnimating(); 
    L.d(TAG, "touch down"); 
  } else if (lastAction == MotionEvent.ACTION_MOVE && ev.getActionMasked() == MotionEvent.ACTION_UP) { 
    // 放開手指,開始回滾 
    isPulling = false; 
    lastAction = -1; 
    startAnimating(); 
    L.d(TAG, "touch up"); 
  } else if (lastAction == MotionEvent.ACTION_DOWN) { 
    if (ev.getActionMasked() == MotionEvent.ACTION_MOVE) { 
      // 在按下手指的基礎上,開始滑動 
      if (isTop && !isPulling) { 
        // listView在頂部而且不處于下拉刷新狀態,開始下拉 
        pullStartY = ev.getY(); 
        lastAction = MotionEvent.ACTION_MOVE; 
        isPulling = true; 
      } 
    } 
  } else if (lastAction == MotionEvent.ACTION_MOVE) { 
    if (isTop) { 
      // 下拉 
      distanceY = ev.getY() - pullStartY; 
      L.d(TAG, distanceY + ""); 
      if (distanceY > 0) { 
        distanceY = (float) (Math.exp(-ev.getY() / pullStartY / 40) * distanceY); 
        // 在下拉狀態時取消系統對move動作的響應,完全由本類響應 
        ev.setAction(MotionEvent.ACTION_DOWN); 
      } else { 
        distanceY = 0; 
        // 在下拉過程中往上拉動該listView使得其回到頂部位置,則將該move動作交由系統進行響應 
        ev.setAction(MotionEvent.ACTION_MOVE); 
      } 
    } else { 
      // 在下拉過程中往上拉動listView使listView往下滾動到其沒有滾動到頂部,則取消其下拉狀態,回到手指按下的初始狀態 
      lastAction = MotionEvent.ACTION_DOWN; 
      isPulling = false; 
      distanceY = 0; 
    } 
  } 
  return super.onTouchEvent(ev); 
}

這一段代碼相對有一點復雜,我們慢慢解析。首先,我們有一個lastAction變量來記錄上一個手勢是什么,有一個isPulling變量來記錄當前PullToRefreshListView是否處于下拉狀態,有一個isTop變量記錄當前PullToRefreshListView是否已經滾動到頂部。

在onTouchEvent方法的重載實現中,一開始PullToRefreshListView沒有接受任何手勢,然后當用戶按下手指出發ACTION_DOWN事件時,我記錄下這個動作,然后當用戶進行滑動時,如果此時PullToRefreshListView沒有“滾動到頂部”,則不做任何處理,反之則將lastAction更新為ACTION_MOVE狀態,更新isPulling變量,記錄當前手指的位置作為計算下拉距離的起始位置,開始下拉刷新,然后在下拉的過程中計算PullToRefreshListView下拉的距離以重畫子View。

在這個手勢處理的實現中,當用戶在下拉過程中突然將PullToRefreshListView往上拉,如果將PullToRefreshListView 拉到不處于“滾動到頂部的狀態”時,則重置下拉狀態,使得:

lastAction = MotionEvent.ACTION_DOWN;

于是PullToRefreshListView接下來的下滑手勢響應權被交還給系統,知道用戶又將PullToRefreshListView下拉到“滾動到頂部”狀態,則又重新執行上述操作,使PullToRefreshListView進入下拉狀態。

5. 如何判斷ListView是否已經滾動到頂部

下一步,我們如何判斷ListView是否處于“滾動到頂部”狀態呢?這一問題我PullToRefreshListView的onScroll中解決。

setOnScrollListener(new OnScrollListener() {
      @Override
      public void onScrollStateChanged(AbsListView view, int scrollState) {
      }
      @Override
      public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
        // 沒有子view的時候(沒有數據,或者被拉到看不到子view),意味著該listView滾動到頂部
        if (getChildCount() == 0) {
          isTop = true;
          return;
        }
        if (firstVisibleItem == 0) {
          View firstView = getChildAt(0);
          if (firstView.getTop() + distanceY >= 0) {
            // 第一個view可見且其相對parent(該listView)的頂部距離大于等于0,意味著該listView也是滾動到頂部
            isTop = true;
            return;
          }
        }
        isTop = false;
      }
    });

為PullToRefreshListView設置一個OnScrollListener回調,并在其onScroll方法中監控其滾動位置,具體看注釋也已經一目了然,我就不多解釋了。

6. 下拉后的回滾動畫

最后,當下拉結束松開手指時,我們需要為PullToRefreshListView執行一個回滾的動畫,我們在onTouchEvent方法中看到:

// ......
    else if (lastAction == MotionEvent.ACTION_MOVE && ev.getActionMasked() == MotionEvent.ACTION_UP) {
      // 放開手指,開始回滾
      isPulling = false;
      lastAction = -1;
      startAnimating();
      L.d(TAG, "touch up");
    }
    // ......

startAnimating方法的實現如下:

  /**
   * 下拉結束時進行回滾動畫并執行刷新動作
   */
  private void startAnimating() {
    int whereToLoad = dp2px(onLoadCallBack.whereToLoad());
    final boolean toLoad;
    if (distanceY <= whereToLoad) {
      pullCancelAnimator = ValueAnimator.ofFloat(distanceY, 0);
      toLoad = false;
    } else {
      pullCancelAnimator = ValueAnimator.ofFloat(distanceY, whereToLoad);
      toLoad = true;
    }
    pullCancelAnimator.setDuration((long) (DEFAULT_BASE_ANIMATING_TIME_PER_100DP*px2dp(distanceY)/100));
    pullCancelAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
    pullCancelAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
      @Override
      public void onAnimationUpdate(ValueAnimator animation) {
        distanceY = (float) animation.getAnimatedValue();
        ViewCompat.postInvalidateOnAnimation(PullToRefreshListView.this);
      }
    });
    pullCancelAnimator.addListener(new Animator.AnimatorListener() {
      @Override
      public void onAnimationStart(Animator animation) {
      }
      @Override
      public void onAnimationEnd(Animator animation) {
        post(new Runnable() {
          @Override
          public void run() {
            pullCancelAnimator = null;
            if (toLoad) {
              onLoadCallBack.onLoad();
            }
          }
        });
      }
      @Override
      public void onAnimationCancel(Animator animation) {
        post(new Runnable() {
          @Override
          public void run() {
            pullCancelAnimator = null;
            if (toLoad) {
              onLoadCallBack.cancelLoad();
            }
          }
        });
      }
      @Override
      public void onAnimationRepeat(Animator animation) {
      }
    });
    pullCancelAnimator.start();
  }

我使用ValueAnimator來實現這一回滾動畫,其中為ValueAnimator設置的回調中,在動畫更新和動畫結束以及動畫取消中分別調用了OnLoadCallBack的3歌回調方法,從而實現PullToRefreshListView的下拉刷新動作。我們可以看到,onLoad方法是在UI線程執行的,因此如果在onLoad方法中執行耗時操作的話,需要在后臺線程中操作,這與我們前面的解析是對應的。

7. 改進和問題

(1) 我們可以將onLoad回調修改成一個返回一個異步任務對象的方法,然后PullToRefreshListView在下拉結束后執行這個異步任務,因此我們就可以不需要cancelLoading回調了,直接就可以在PullToRefreshListView內部進行取消操作,這樣做可以增強封裝性,但相對目前的做法自由度就沒有那么高了。

(2) 回滾動畫應該也可以進行優化,具體怎么優化我也不清楚。。。各位朋友有好的想法可以在評論區提議一下,謝謝~

(3) 下拉的時候對多點觸碰的響應并不完美,雖然也可以接受,但是做不到像qq客戶端的聊天列表那樣。

8. 源碼

至此,我已經解析了如何實現一個下拉刷新列表,PullToRefreshListView的源碼如下。

import android.animation.Animator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.support.v4.view.ViewCompat;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.widget.AbsListView;
import android.widget.ListView;
import com.ivan.healthcare.healthcare_android.log.L;
/**
 * 支持下拉刷新的的listView
 * Created by Ivan on 16/2/14.
 */
public class PullToRefreshListView extends ListView {
  private final String TAG = "PullToRefreshListView";
  private final int DEFAULT_BASE_ANIMATING_TIME_PER_100DP = 150;
  public static final int DEFAULT_WHERE_TO_LOAD = 80;
  private int lastAction = -1;
  private float pullStartY = -1;
  private boolean isTop = true;
  private float distanceY = 0;
  private boolean isPulling = false;
  private ValueAnimator pullCancelAnimator;
  private Context context;
  private Drawable refreshDrawable;
  private OnLoadCallBack onLoadCallBack = new OnLoadCallBack() {
    @Override
    public int whereToLoad() {
      return DEFAULT_WHERE_TO_LOAD;
    }
    @Override
    public void onLoad() {
    }
    @Override
    public void cancelLoad() {
    }
    @Override
    public Drawable refreshDrawable() {
      return null;
    }
  };
  public PullToRefreshListView(Context context) {
    super(context);
    initView(context);
  }
  public PullToRefreshListView(Context context, AttributeSet attrs) {
    super(context, attrs);
    initView(context);
  }
  private void initView(Context context) {
    this.context = context;
    setOnScrollListener(new OnScrollListener() {
      @Override
      public void onScrollStateChanged(AbsListView view, int scrollState) {
      }
      @Override
      public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
        // 沒有子view的時候(沒有數據,或者被拉到看不到子view),意味著該listView滾動到頂部
        if (getChildCount() == 0) {
          isTop = true;
          return;
        }
        if (firstVisibleItem == 0) {
          View firstView = getChildAt(0);
          if (firstView.getTop() + distanceY >= 0) {
            // 第一個view可見且其相對parent(該listView)的頂部距離大于等于0,意味著該listView也是滾動到頂部
            isTop = true;
            return;
          }
        }
        isTop = false;
      }
    });
  }
  @Override
  public boolean onTouchEvent(MotionEvent ev) {
    if (lastAction == -1 && ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
      // 按下的時候
      lastAction = MotionEvent.ACTION_DOWN;
      cancelAnimating();
      L.d(TAG, "touch down");
    } else if (lastAction == MotionEvent.ACTION_MOVE && ev.getActionMasked() == MotionEvent.ACTION_UP) {
      // 放開手指,開始回滾
      isPulling = false;
      lastAction = -1;
      startAnimating();
      L.d(TAG, "touch up");
    } else if (lastAction == MotionEvent.ACTION_DOWN) {
      if (ev.getActionMasked() == MotionEvent.ACTION_MOVE) {
        // 在按下手指的基礎上,開始滑動
        if (isTop && !isPulling) {
          // listView在頂部而且不處于下拉刷新狀態,開始下拉
          pullStartY = ev.getY();
          lastAction = MotionEvent.ACTION_MOVE;
          isPulling = true;
        }
      }
    } else if (lastAction == MotionEvent.ACTION_MOVE) {
      if (isTop) {
        // 下拉
        distanceY = ev.getY() - pullStartY;
        L.d(TAG, distanceY + "");
        if (distanceY > 0) {
          distanceY = (float) (Math.exp(-ev.getY() / pullStartY / 40) * distanceY);
          // 在下拉狀態時取消系統對move動作的響應,完全由本類響應
          ev.setAction(MotionEvent.ACTION_DOWN);
        } else {
          distanceY = 0;
          // 在下拉過程中往上拉動該listView使得其回到頂部位置,則將該move動作交由系統進行響應
          ev.setAction(MotionEvent.ACTION_MOVE);
        }
      } else {
        // 在下拉過程中往上拉動listView使listView往下滾動到其沒有滾動到頂部,則取消其下拉狀態,回到手指按下的初始狀態
        lastAction = MotionEvent.ACTION_DOWN;
        isPulling = false;
        distanceY = 0;
      }
    }
    return super.onTouchEvent(ev);
  }
  @Override
  protected void dispatchDraw(Canvas canvas) {
    super.dispatchDraw(canvas);
    if (distanceY > 0) {
      if (refreshDrawable == null) {
        refreshDrawable = onLoadCallBack.refreshDrawable();
      }
      if (refreshDrawable == null) {
        canvas.drawColor(Color.GRAY);
      } else {
        int left = getPaddingLeft();
        int top = getPaddingTop();
        refreshDrawable.setBounds(left, top, getWidth()+left, getHeight()+top);
        refreshDrawable.draw(canvas);
      }
      canvas.save();
      canvas.translate(getPaddingLeft(), getPaddingTop() + distanceY);
      for (int i=0;i<getChildCount();i++) {
        View child = getChildAt(i);
        drawChild(canvas, child, getDrawingTime());
      }
      canvas.restore();
    }
  }
  /**
   * 下拉結束時進行回滾動畫并執行刷新動作
   */
  private void startAnimating() {
    int whereToLoad = dp2px(onLoadCallBack.whereToLoad());
    final boolean toLoad;
    if (distanceY <= whereToLoad) {
      pullCancelAnimator = ValueAnimator.ofFloat(distanceY, 0);
      toLoad = false;
    } else {
      pullCancelAnimator = ValueAnimator.ofFloat(distanceY, whereToLoad);
      toLoad = true;
    }
    pullCancelAnimator.setDuration((long) (DEFAULT_BASE_ANIMATING_TIME_PER_100DP*px2dp(distanceY)/100));
    pullCancelAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
    pullCancelAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
      @Override
      public void onAnimationUpdate(ValueAnimator animation) {
        distanceY = (float) animation.getAnimatedValue();
        ViewCompat.postInvalidateOnAnimation(PullToRefreshListView.this);
      }
    });
    pullCancelAnimator.addListener(new Animator.AnimatorListener() {
      @Override
      public void onAnimationStart(Animator animation) {
      }
      @Override
      public void onAnimationEnd(Animator animation) {
        post(new Runnable() {
          @Override
          public void run() {
            pullCancelAnimator = null;
            if (toLoad) {
              onLoadCallBack.onLoad();
            }
          }
        });
      }
      @Override
      public void onAnimationCancel(Animator animation) {
        post(new Runnable() {
          @Override
          public void run() {
            pullCancelAnimator = null;
            if (toLoad) {
              onLoadCallBack.cancelLoad();
            }
          }
        });
      }
      @Override
      public void onAnimationRepeat(Animator animation) {
      }
    });
    pullCancelAnimator.start();
  }
  private void cancelAnimating() {
    if (pullCancelAnimator != null) {
      pullCancelAnimator.cancel();
    }
  }
  private float px2dp(float pxvalue) {
    return (pxvalue - 0.5f) /context.getResources().getDisplayMetrics().density;
  }
  private int dp2px(float dpvalue) {
    return (int) (dpvalue * context.getResources().getDisplayMetrics().density + 0.5f);
  }
  /**
   * 下拉刷新的回調
   */
  public interface OnLoadCallBack {
    /**
     * 下拉結束后將listView定位到哪個位置等待刷新完成
     * @return listView的定位y坐標值,in dp
     */
    int whereToLoad();
    /**
     * 下拉結束后進行刷新的回調
     */
    void onLoad();
    /**
     * 取消刷新
     */
    void cancelLoad();
    /**
     * 下拉刷新的背景
     * @return 背景drawable
     */
    Drawable refreshDrawable();
  }
  /**
   * 設置下拉刷新回調
   * @param cb 回調
   */
  public void setOnLoadCallBack(OnLoadCallBack cb) {
    this.onLoadCallBack = cb;
  }
  /**
   * 刷新動作結束后調用該方法結束刷新,使得listView回滾到頂部
   */
  public void setLoadingFinish() {
    startAnimating();
  }
}

看完上述內容,你們掌握android中怎么實現一個阻尼下拉刷新列表的方法了嗎?如果還想學到更多技能或想了解更多相關內容,歡迎關注億速云行業資訊頻道,感謝各位的閱讀!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

新绛县| 双牌县| 慈利县| 浙江省| 峨眉山市| 若羌县| 兴义市| 通海县| 郯城县| 浙江省| 通化市| 汶川县| 定南县| 新乐市| 邛崃市| 静海县| 泰顺县| 江西省| 肥城市| 清新县| 云南省| 峨山| 阿克| 托克托县| 渝北区| 石渠县| 胶南市| 平顶山市| 石台县| 邵阳市| 阜宁县| 南涧| 娄底市| 土默特右旗| 乳源| 九龙坡区| 嘉荫县| 桃园县| 舒城县| 元谋县| 岢岚县|