English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Реализация下拉 обновления layouts с помощьюRefreshLayout в Android

В проекте нужна функция обновления при прокрутке вниз, но этот View не ListView и подобный виджет, нужно реализовать эту функцию через ViewGroup, вначале я немного поискал в Интернете, но не нашел ничего особенно подходящего, и код был не очень понятен, поэтому решил написать это сам

  Затем вытащил исходный код XlistView и начал его постепенно изучать, а затем大致 понял исходный код XLisview, и наконец решил взяться за это самому

  Для удобства headView все равно использовал HeadView XListView, это значительно упростило работу:)

  Обновление при прокрутке вниз, обновление при прокрутке вниз,肯定是先实现下拉功能, вначале я планировал реализовать это через extends ScrollView, потому что у него есть готовый эффект прокрутки, но на практике отказался по двум причинам:

1、Под ScrollView может быть только один подвид View, хотя можно добавить ViewGroup под Scroll, и динамически добавить headView в передний ViewGroup, но я все равно предпочитаю визуальный предпросмотр Studio, потому что это не так trựcуточно!

2、  При嵌入 ListView в ScrollView может произойти冲突, поэтому нужно перезаписать ListView. Поэтому отказался и перешел к другому подходу!

 О причинах выше 1: динамически добавлять headView в GroupView ScrollView, можно перезаписать метод onViewAdded() ScrollView, чтобы добавить headView в подгруппу View

@Override 
public void onViewAdded(View child) { 
  super.onViewAdded(child); 
  //Потому что headView должен быть сверху, поэтому сначала подумал о Vertical LinearLayout 
  LinearLayout linearLayout = (LinearLayout) getChildAt(0); 
  linearLayout.addView(view, 0); 
} 

  Измените мышление, через extends LinearLayout!
Сначала подготовим, нам нужен headerView, который нужно получить высоту, а также исходная высота layout

private void initView(Context context) { 
   mHeaderView = new SRefreshHeader(context); 
   mHeaderViewContent = (RelativeLayout) mHeaderView.findViewById(R.id.slistview_header_content); 
   setOrientation(VERTICAL); 
   addView(mHeaderView, 0); 
   getHeaderViewHeight(); 
   getViewHeight(); 
 } 

mHeaderView = new SRefreshHeader(context);
Инстанцирование headerView через конструктор

mHeaderViewContent = (RelativeLayout)

mHeaderView.findViewById(R.id.slistview_header_content);

 Это разбор содержимого headerView, через которую мы будем определять высоту, вы, наверное, спросите, почему не использовать mHeaderView для определения высоты, зайдите в метод конструктора и посмотрите следующий код

// Инициализация, установить высоту виджета для обновления下拉 на 0 
LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT, 0); 
mContainer = (LinearLayout) LayoutInflater.from(context).inflate(R.layout.listview_head_view_layout, null); 
w(mContainer, lp); 

Если напрямую получить высоту mHeaderView, то это肯定是 0
getHeaderViewHeight();
getViewHeight();
Это получение высоты headerView и исходной высоты layout

/** 
  * Получение высоты headView 
  */ 
  private void getHeaderViewHeight() { 
    ViewTreeObserver vto2 = mHeaderViewContent.getViewTreeObserver(); 
    vto2.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { 
      @Override 
      public void onGlobalLayout() { 
        mHeaderViewHeight = mHeaderViewContent.getHeight(); 
        mHeaderViewContent.getViewTreeObserver().removeGlobalOnLayoutListener(this); 
      } 
    }); 
  } 
  /** 
  * Получение текущего экземпляра высоты SRefreshLayout 
  */ 
  private void getViewHeight() { 
    ViewTreeObserver thisView = getViewTreeObserver(); 
    thisView.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { 
      @Override 
      public void onGlobalLayout() { 
        SRefreshLayout.this.mHeight = SRefreshLayout.this.getHeight(); 
        SRefreshLayout.this.getViewTreeObserver().removeGlobalOnLayoutListener(this); 
      } 
    }); 
  } 

Подготовка завершена,接下来 нужно выполнить действие прокрутки вниз
До этого момента, наверняка сразу приходит на ум метод onTouchEvent(), да! Начнем работу именно здесь

Реализация прокрутки вниз проходит через три этапа
ACTION_UP → ACTION_MOVE → ACTION_UP
В событии ACTION_UP, то есть когда палец нажимается, нам нужно просто запомнить координаты нажатия

switch (ev.getAction()) { 
      case MotionEvent.ACTION_DOWN: 
        // Запомнить начальную высоту 
        mLastY = ev.getRawY(); // Запомнить координату Y при нажатии 
        break; 

Затем событие ACTION_MOVE, это наиболее важное, так как изменения высоты HeadView и layout происходят именно здесь

case MotionEvent.ACTION_MOVE: 
       if (!isRefreashing) 
         isRefreashing = true; 
       final float deltaY = ev.getRawY() - mLastY; 
       mLastY = ev.getRawY(); 
       updateHeaderViewHeight(deltaY / 1.8f); // По определенному коэффициенту уменьшить расстояние перемещения 
       updateHeight(); 
       break; 

Внутри updateHeaderViewHeight и updateHeight изменяются высота headerView и высота layout

 private void updateHeight() { 
    ViewGroup.LayoutParams lp = getLayoutParams(); 
    //更新当前layout实例高度为headerView高度加上最初的layout高度 
    //如果不更新layout 会造成内容高度压缩 无法保持比例 
    lp.height = (mHeight + mHeaderView.getVisiableHeight()); 
    setLayoutParams(lp); 
  } 
  private void updateHeaderViewHeight(float space) { 
//    if (space < 0) 
//      space = 0; 
//    int factHeight = (int) (space - mHeaderViewHeight); 
    if (mHeaderView.getStatus() != SRefreshHeader.STATE_REFRESHING) { 
      //如果不处于刷新中同时如果高度 
      if (mHeaderView.getVisiableHeight() < mHeaderViewHeight * 2 && mHeaderView.getStatus() != SRefreshHeader.STATE_NORMAL) { 
        mHeaderView.setState(SRefreshHeader.STATE_NORMAL); 
      } 
      if (mHeaderView.getVisiableHeight() > mHeaderViewHeight * 2 && mHeaderView.getStatus() != SRefreshHeader.STATE_READY) { 
        mHeaderView.setState(SRefreshHeader.STATE_READY); 
      } 
    } 
    mHeaderView.setVisiableHeight((int) space 
        + mHeaderView.getVisiableHeight()); 
  } 

При обновлении высоты Header, мы определяем, достигли ли мы расстояния обновления, используя расстояние прокрутки, в коде я установил, что если расстояние достигает двойной высоты mHeaderView, то мы переходим в состояние «освободить обновление», если нет, то мы保持在 состоянии «прокрутка вниз для обновления»
В headerView установлено три состояния,分别是

public final static int STATE_NORMAL = 0;// pull to refresh 
 public final static int STATE_READY = 1;// ready to refresh 
 public final static int STATE_REFRESHING = 2;// refreshing 

Метод обновления высоты headerView и layout одинаковый, это просто добавление расстояния перемещения к исходной высоте и повторное присвоение heights headerView или layout

mHeaderView.setVisiableHeight((int) space 
               + mHeaderView.getVisiableHeight()); 

И, наконец, событие ACTION_UP, которое возникает, когда палец отрывается от экрана, здесь нам нужно определить конечное состояние headerView на основе текущего состояния headerView!

case MotionEvent.ACTION_UP: 
        // При отпускании 
        // Избегать триггера события нажатия 
        if (!isRefreashing) 
          break; 
        // Если состояние headView находится в состоянии READY, то при отпускании он должен перейти в состояние REFRESHING 
        if (mHeaderView.getStatus() == SRefreshHeader.STATE_READY) { 
          mHeaderView.setState(SRefreshHeader.STATE_REFRESHING); 
        } 
        // В зависимости от состояния восстановить текущий экземпляр SrefreshLayout и высоту headView 
        resetHeadView(mHeaderView.getStatus()); 
        reset(mHeaderView.getStatus()); 
        mLastY = -1; // Восстановить координаты 
        break; 

resetHeadView和reset分别是重置headerView高度和layout高度的方法

private void reset(int status) { 
    ViewGroup.LayoutParams lp = getLayoutParams(); 
    switch (status) { 
      case SRefreshHeader.STATE_REFRESHING: 
        lp.height = mHeight + mHeaderViewHeight; 
        break; 
      case SRefreshHeader.STATE_NORMAL: 
        lp.height = mHeight; 
        break; 
    } 
    setLayoutParams(lp); 
  } 
  private void resetHeadView(int status) { 
    switch (status) { 
      case SRefreshHeader.STATE_REFRESHING: 
        mHeaderView.setVisiableHeight(mHeaderViewHeight);} 
        break; 
      case SRefreshHeader.STATE_NORMAL: 
        mHeaderView.setVisiableHeight(0); 
        break; 
    } 
  } 

实现方式也是一样的。根据状态来判断,如果是处于刷新中,那headerView应该正常显示,并且高度是初始的高度,如果处于NORMAL,也就是"下拉刷新"状态,那么说未触发刷新,重置时,headerView应该被隐藏,也就是高度重置为0

到这里下拉刷新操作也基本完成了,还需要加一个回调接口进行通知

interface OnRefreshListener { 
    void onRefresh(); 
  } 
case MotionEvent.ACTION_UP: 
        // При отпускании 
        // Избегать триггера события нажатия 
        if (!isRefreashing) 
          break; 
        // Если состояние headView находится в состоянии READY, то при отпускании он должен перейти в состояние REFRESHING 
        if (mHeaderView.getStatus() == SRefreshHeader.STATE_READY) { 
          mHeaderView.setState(SRefreshHeader.STATE_REFRESHING); 
          if (mOnRefreshListener != null) 
            mOnRefreshListener.onRefresh(); 
        } 
        // В зависимости от состояния восстановить текущий экземпляр SrefreshLayout и высоту headView 
        resetHeadView(mHeaderView.getStatus()); 
        reset(mHeaderView.getStatus()); 
        mLastY = -1; // Восстановить координаты 
        break; 

好,到这里就基本完成了,试试效果吧。咦,发现一个问题,嵌套ListView的时候为什么这个Layout不能执行下拉刷新!仔细想想应该是事件分发的问题,还需要处理一下事件的拦截!
关于事件拦截的处理,阅读了鸿洋大神写的viewgroup事件分发的博客和Android-Ultra-Pull-To-Refresh的部分源码,从中找到了解决办法:

@Override 
  public boolean onInterceptTouchEvent(MotionEvent ev) { 
    AbsListView absListView = null; 
    for (int n = 0; n < getChildCount(); n++) { 
      if (getChildAt(n) instanceof AbsListView) { 
        absListView = (ListView) getChildAt(n); 
        Logs.v("найден listView"); 
      } 
    } 
    if (absListView == null) 
      return super.onInterceptTouchEvent(ev); 
    switch (ev.getAction()) { 
      case MotionEvent.ACTION_DOWN: 
        mStartY = ev.getRawY(); 
        break; 
      case MotionEvent.ACTION_MOVE: 
        float space = ev.getRawY() - mStartY; 
        Logs.v("space:" + space); 
        if (space > 0 && !absListView.canScrollVertically(-1) && absListView.getFirstVisiblePosition() == 0) { 
          Logs.v("блокировка успешна"); 
          return true; 
        } else { 
          Logs.v("не блокировать"); 
          return false; 
        } 
    } 
    return super.onInterceptTouchEvent(ev); 
  } 

其中

if (space > 0 && !absListView.canScrollVertically(-1) && absListView.getFirstVisiblePosition() == 0)
space即移动的距离 canScrollVertically()是判断ListView能否在垂直方向上滚动,参数为负数时代表向上,为正数时代码向下滚动,最后一个就是ListView第一个可见的item的postion

加上上面的事件拦截处理,一个可以满足开头提到的需求的Viewgroup也就完成了!

下面贴上Layout的源码和HeaderView(直接使用的XlistView的HeaderView)的源码

public class SRefreshLayout extends LinearLayout { 
  private SRefreshHeader mHeaderView; 
  private RelativeLayout mHeaderViewContent; 
  private boolean isRefreashing; 
  private float mLastY = -1; // 按下的起始高度 
  private int mHeaderViewHeight; // headerView内容高度 
  private int mHeight;// размер макета 
  private float mStartY; 
  interface OnRefreshListener { 
    void onRefresh(); 
  } 
  public OnRefreshListener mOnRefreshListener; 
  public SRefreshLayout(Context context) { 
    super(context); 
    initView(context); 
  } 
  public SRefreshLayout(Context context, AttributeSet attrs) { 
    super(context, attrs); 
    initView(context); 
  } 
  public SRefreshLayout(Context context, AttributeSet attrs, int defStyleAttr) { 
    super(context, attrs, defStyleAttr); 
    initView(context); 
  } 
  private void initView(Context context) { 
    mHeaderView = new SRefreshHeader(context); 
    mHeaderViewContent = (RelativeLayout) mHeaderView.findViewById(R.id.slistview_header_content); 
    setOrientation(VERTICAL); 
    addView(mHeaderView, 0); 
    getHeaderViewHeight(); 
    getViewHeight(); 
  } 
  /** 
   * Получение высоты headView 
   */ 
  private void getHeaderViewHeight() { 
    ViewTreeObserver vto2 = mHeaderViewContent.getViewTreeObserver(); 
    vto2.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { 
      @Override 
      public void onGlobalLayout() { 
        mHeaderViewHeight = mHeaderViewContent.getHeight(); 
        mHeaderViewContent.getViewTreeObserver().removeGlobalOnLayoutListener(this); 
      } 
    }); 
  } 
  /** 
   * Получение текущего экземпляра высоты SRefreshLayout 
   */ 
  private void getViewHeight() { 
    ViewTreeObserver thisView = getViewTreeObserver(); 
    thisView.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { 
      @Override 
      public void onGlobalLayout() { 
        SRefreshLayout.this.mHeight = SRefreshLayout.this.getHeight(); 
        SRefreshLayout.this.getViewTreeObserver().removeGlobalOnLayoutListener(this); 
      } 
    }); 
  } 
  @Override 
  public boolean onInterceptTouchEvent(MotionEvent ev) { 
    AbsListView absListView = null; 
    for (int n = 0; n < getChildCount(); n++) { 
      if (getChildAt(n) instanceof AbsListView) { 
        absListView = (ListView) getChildAt(n); 
        Logs.v("найден listView"); 
      } 
    } 
    if (absListView == null) 
      return super.onInterceptTouchEvent(ev); 
    switch (ev.getAction()) { 
      case MotionEvent.ACTION_DOWN: 
        mStartY = ev.getRawY(); 
        break; 
      case MotionEvent.ACTION_MOVE: 
        float space = ev.getRawY() - mStartY; 
        Logs.v("space:" + space); 
        if (space > 0 && !absListView.canScrollVertically(-1) && absListView.getFirstVisiblePosition() == 0) { 
          Logs.v("блокировка успешна"); 
          return true; 
        } else { 
          Logs.v("не блокировать"); 
          return false; 
        } 
    } 
    return super.onInterceptTouchEvent(ev); 
  } 
  @Override 
  public boolean onTouchEvent(MotionEvent ev) { 
    if (mLastY == -1) 
      mLastY = ev.getRawY(); 
    switch (ev.getAction()) { 
      case MotionEvent.ACTION_DOWN: 
        // Запомнить начальную высоту 
        mLastY = ev.getRawY(); // Запомнить координату Y при нажатии 
        break; 
      // При отрыве пальца от экрана 
      case MotionEvent.ACTION_UP: 
        // При отпускании 
        // Избегать триггера события нажатия 
        if (!isRefreashing) 
          break; 
        // Если состояние headView находится в состоянии READY, то при отпускании он должен перейти в состояние REFRESHING 
        if (mHeaderView.getStatus() == SRefreshHeader.STATE_READY) { 
          mHeaderView.setState(SRefreshHeader.STATE_REFRESHING); 
          if (mOnRefreshListener != null) 
            mOnRefreshListener.onRefresh(); 
        } 
        // В зависимости от состояния восстановить текущий экземпляр SrefreshLayout и высоту headView 
        resetHeadView(mHeaderView.getStatus()); 
        reset(mHeaderView.getStatus()); 
        mLastY = -1; // Восстановить координаты 
        break; 
      case MotionEvent.ACTION_MOVE: 
        if (!isRefreashing) 
          isRefreashing = true; 
        final float deltaY = ev.getRawY() - mLastY; 
        mLastY = ev.getRawY(); 
        updateHeaderViewHeight(deltaY / 1.8f); // По определенному коэффициенту уменьшить расстояние перемещения 
        updateHeight(); 
        break; 
    } 
    return super.onTouchEvent(ev); 
  } 
  private void reset(int status) { 
    ViewGroup.LayoutParams lp = getLayoutParams(); 
    switch (status) { 
      case SRefreshHeader.STATE_REFRESHING: 
        lp.height = mHeight + mHeaderViewHeight; 
        break; 
      case SRefreshHeader.STATE_NORMAL: 
        lp.height = mHeight; 
        break; 
    } 
    setLayoutParams(lp); 
  } 
  private void resetHeadView(int status) { 
    switch (status) { 
      case SRefreshHeader.STATE_REFRESHING: 
        mHeaderView.setVisiableHeight(mHeaderViewHeight);} 
        break; 
      case SRefreshHeader.STATE_NORMAL: 
        mHeaderView.setVisiableHeight(0); 
        break; 
    } 
  } 
  private void updateHeight() { 
    ViewGroup.LayoutParams lp = getLayoutParams(); 
    //更新当前layout实例高度为headerView高度加上最初的layout高度 
    //如果不更新layout 会造成内容高度压缩 无法保持比例 
    lp.height = (mHeight + mHeaderView.getVisiableHeight()); 
    setLayoutParams(lp); 
  } 
  private void updateHeaderViewHeight(float space) { 
//    if (space < 0) 
//      space = 0; 
//    int factHeight = (int) (space - mHeaderViewHeight); 
    if (mHeaderView.getStatus() != SRefreshHeader.STATE_REFRESHING) { 
      //如果不处于刷新中同时如果高度 
      if (mHeaderView.getVisiableHeight() < mHeaderViewHeight * 2 && mHeaderView.getStatus() != SRefreshHeader.STATE_NORMAL) { 
        mHeaderView.setState(SRefreshHeader.STATE_NORMAL); 
      } 
      if (mHeaderView.getVisiableHeight() > mHeaderViewHeight * 2 && mHeaderView.getStatus() != SRefreshHeader.STATE_READY) { 
        mHeaderView.setState(SRefreshHeader.STATE_READY); 
      } 
    } 
    mHeaderView.setVisiableHeight((int) space 
        + mHeaderView.getVisiableHeight()); 
  } 
  public void stopRefresh() { 
    if (mHeaderView.getStatus() == SRefreshHeader.STATE_REFRESHING) { 
      mHeaderView.setState(SRefreshHeader.STATE_NORMAL); 
      resetHeadView(SRefreshHeader.STATE_NORMAL); 
      reset(SRefreshHeader.STATE_NORMAL); 
    } 
  } 
  public void setOnRefreshListener(OnRefreshListener onRefreshListener) { 
    this.mOnRefreshListener = onRefreshListener; 
  } 
} 
public class SRefreshHeader extends LinearLayout { 
  private LinearLayout mContainer; 
  private int mState = STATE_NORMAL; 
  private Animation mRotateUpAnim; 
  private Animation mRotateDownAnim; 
  private final int ROTATE_ANIM_DURATION = 500; 
  public final static int STATE_NORMAL = 0;// pull to refresh 
  public final static int STATE_READY = 1;// ready to refresh 
  public final static int STATE_REFRESHING = 2;// refreshing 
  private ImageView mHeadArrowImage; 
  private TextView mHeadLastRefreashTimeTxt; 
  private TextView mHeadHintTxt; 
  private TextView mHeadLastRefreashTxt; 
  private ProgressBar mRefreshingProgress; 
  public SRefreshHeader(Context context) { 
    super(context); 
    initView(context); 
  } 
  /** 
   * @param context 
   * @param attrs 
   */ 
  public SRefreshHeader(Context context, AttributeSet attrs) { 
    super(context, attrs); 
    initView(context); 
  } 
  private void initView(Context context) { 
    // Инициализация, установить высоту виджета для обновления下拉 на 0 
    LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT, 0); 
    mContainer = (LinearLayout) LayoutInflater.from(context).inflate(R.layout.listview_head_view_layout, null); 
    addView(mContainer, lp); 
    setGravity(Gravity.BOTTOM); 
    mHeadArrowImage = (ImageView) findViewById(R.id.slistview_header_arrow); 
    mHeadLastRefreashTimeTxt = (TextView) findViewById(R.id.slistview_header_time); 
    mHeadHintTxt = (TextView) findViewById(R.id.slistview_header_hint_text); 
    mHeadLastRefreashTxt = (TextView) findViewById(R.id.slistview_header_last_refreash_txt); 
    mRefreshingProgress = (ProgressBar) findViewById(R.id.slistview_header_progressbar); 
    mRotateUpAnim = new RotateAnimation(0.0f, -180.0f, 
        Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 
        0.5f); 
    mRotateUpAnim.setDuration(ROTATE_ANIM_DURATION); 
    mRotateUpAnim.setFillAfter(true); 
    mRotateDownAnim = new RotateAnimation(-180.0f, 0.0f, 
        Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 
        0.5f); 
    mRotateDownAnim.setDuration(ROTATE_ANIM_DURATION); 
    mRotateDownAnim.setFillAfter(true); 
  } 
  public void setState(int state) { 
    if (state == mState) return; 
    if (state == STATE_REFRESHING) {  // Показать прогресс 
      mHeadArrowImage.clearAnimation(); 
      mHeadArrowImage.setVisibility(View.INVISIBLE); 
      mRefreshingProgress.setVisibility(View.VISIBLE); 
    } else {  // Показать изображение стрелки 
      mHeadArrowImage.setVisibility(View.VISIBLE); 
      mRefreshingProgress.setVisibility(View.INVISIBLE); 
    } 
    switch (state) { 
      case STATE_NORMAL: 
        if (mState == STATE_READY) { 
          mHeadArrowImage.startAnimation(mRotateDownAnim); 
        } 
        if (mState == STATE_REFRESHING) { 
          mHeadArrowImage.clearAnimation(); 
        } 
        mHeadHintTxt.setText("Прогните вниз для обновления "); 
        break; 
      case STATE_READY: 
        if (mState != STATE_READY) { 
          mHeadArrowImage.clearAnimation(); 
          mHeadArrowImage.startAnimation(mRotateUpAnim); 
          mHeadHintTxt.setText("Отпустите, чтобы обновить "); 
        } 
        break; 
      case STATE_REFRESHING: 
        mHeadHintTxt.setText("Загрузка... "); 
        break; 
      default: 
    } 
    mState = state; 
  } 
  public void setVisiableHeight(int height) { 
    if (height < 0) 
      height = 0; 
    LayoutParams lp = (LayoutParams) mContainer 
        .getLayoutParams(); 
    lp.height = height; 
    mContainer.setLayoutParams(lp); 
  } 
  public int getStatus() { 
    return mState; 
  } 
  public int getVisiableHeight() { 
    return mContainer.getHeight(); 
  } 
} 

В конце файл разметки

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
  android:layout_width="match_parent" 
  android:layout_height="wrap_content" 
  android:gravity="bottom"> 
  <RelativeLayout 
    android:id="@+id/slistview_header_content" 
    android:layout_width="match_parent" 
    android:layout_height="60dp"> 
    <LinearLayout 
      android:id="@+id/slistview_header_text" 
      android:layout_width="wrap_content" 
      android:layout_height="wrap_content" 
      android:layout_centerInParent="true" 
      android:gravity="center" 
      android:orientation="vertical"> 
      <TextView 
        android:id="@+id/slistview_header_hint_text" 
        android:layout_width="wrap_content" 
        android:layout_height="wrap_content" 
        android:text="下拉刷新" /> 
      <LinearLayout 
        android:layout_width="wrap_content" 
        android:layout_height="wrap_content" 
        android:layout_marginTop="3dp"> 
        <TextView 
          android:id="@+id/slistview_header_last_refreash_txt" 
          android:layout_width="wrap_content" 
          android:layout_height="wrap_content" 
          android:text="上次刷新时间" 
          android:textSize="12sp" /> 
        <TextView 
          android:id="@+id/slistview_header_time" 
          android:layout_width="wrap_content" 
          android:layout_height="wrap_content" 
          android:textSize="12sp" /> 
      </LinearLayout> 
    </LinearLayout> 
    <ProgressBar 
      android:id="@+id/slistview_header_progressbar" 
      android:layout_width="30dp" 
      android:layout_height="30dp" 
      android:layout_centerVertical="true" 
      android:layout_toLeftOf="@id/slistview_header_text" 
      android:visibility="invisible" /> 
    <ImageView 
      android:id="@+id/slistview_header_arrow" 
      android:layout_width="wrap_content" 
      android:layout_height="wrap_content" 
      android:layout_alignLeft="@id/slistview_header_progressbar" 
      android:layout_centerVertical="true" 
      android:layout_toLeftOf="@id/slistview_header_text" 
      android:src="@drawable/mmtlistview_arrow" /> 
  </RelativeLayout> 
</LinearLayout> 

Вот и все, что было в этой статье, надеюсь, это поможет вам в изучении.我们也希望大家多多支持呐喊教程。

Объявление: содержимое этой статьи взято из Интернета, авторские права принадлежат соответствующему автору. Контент предоставлен пользователями Интернета, сайт не обладает правами собственности, не был отредактирован вручную и не несет ответственности за него. Если вы обнаружите подозрительное нарушение авторских прав, пожалуйста, отправьте письмо по адресу: notice#oldtoolbag.com (при отправке письма замените # на @) для отчета,并提供 соответствующие доказательства. В случае подтверждения мы немедленно удалим涉嫌侵权的内容.

Основной учебник
Рекомендуется к просмотру