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

Implementação do Layout de Atualização de Android (RefreshLayout) para Arrastar para Baixo e Atualizar

No projeto, é necessário a função de atualização de arrasto, mas este View não é um controle do tipo ListView, precisa implementar essa função através de ViewGroup, inicialmente procurei um pouco na internet, não encontrei nada especialmente adequado, também não entendi muito o código, então decidi escrever um por conta própria.

  Então, tirei o código-fonte do XlistView para ver um pouco a cada vez, e finalmente compreendi o código-fonte do XLisview, decidi finalmente fazer isso eu mesmo

  Para facilitar, a headView ainda usou HeadView do XListView, economizando muito trabalho:)

  Atualização de arrasto, atualização de arrasto, é claro que é necessário implementar a função de arrasto primeiro, inicialmente eu estava pensando em usar extends ScrollView para implementar, porque há efeitos de rolagem prontos, mas finalmente desisti por dois motivos:

1, Only one child View can be under ScrollView, although you can add a ViewGroup under Scroll and then dynamically add headView to the front ViewGroup, but I still prefer studio's visual preview, I always feel it's not intuitive!

2, When a ListView is nested within a ScrollView, a conflict will occur, and it is also necessary to rewrite ListView. So, I give up and change the idea!

 Regarding the above reason1: Dynamically add headView to the ScrollView's middle GroupView, you can override the ScrollView's onViewAdded() method to add the initialized headView to the child GroupView

@Override 
public void onViewAdded(View child) { 
  super.onViewAdded(child); 
  //Since headView needs to be on top, the first thought is Vertical LinearLayout 
  LinearLayout linearLayout = (LinearLayout) getChildAt(0); 
  linearLayout.addView(view, 0); 
} 

  Change the thinking, implement it by extending LinearLayout!
First do the preparation work, we need a HeaderView and the height of HeaderView to be obtained, as well as the initial height of 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 is instantiated through the constructor

mHeaderViewContent = (RelativeLayout)

mHeaderView.findViewById(R.id.slistview_header_content);

 This is to parse the headerView content areaiew, I'll get the height of this view in a moment, you must be wondering why not use the above mHeaderView to get the height, you can see the following code by entering the constructor method

// Em situação inicial, definir a altura da view de atualização下拉 para 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); 

If you directly get the height of mHeaderView, it must be 0
getHeaderViewHeight();
getViewHeight();
It is to obtain the height of HeaderView and the initial height of Layout

/** 
  * Obter a altura do headView 
  */ 
  private void getHeaderViewHeight() { 
    ViewTreeObserver vto2 = mHeaderViewContent.getViewTreeObserver(); 
    vto2.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { 
      @Override 
      public void onGlobalLayout() { 
        mHeaderViewHeight = mHeaderViewContent.getHeight(); 
        mHeaderViewContent.getViewTreeObserver().removeGlobalOnLayoutListener(this); 
      } 
    }); 
  } 
  /** 
  * Obter a altura da instância atual de 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); 
      } 
    }); 
  } 

Preparation is complete, next is to perform the pull-down operation
By now, you must have thought of the on_TOUCH_EVENT() method, that's right! Let's start building here now

To implement pull down, it will go through three processes
ACTION_UP→ACTION_MOVE→ACTION_UP
No evento ACTION_UP, ou seja, quando o dedo é pressionado, tudo o que precisamos fazer é registrar a coordenada do toque quando foi pressionado

switch (ev.getAction()) { 
      case MotionEvent.ACTION_DOWN: 
        //记录起始高度 
        mLastY = ev.getRawY();//记录按下时的Y坐标 
        break; 

Depois disso, é o evento ACTION_MOVE, que é o mais importante, pois as mudanças de altura do HeadView e do Layout ao puxar para baixo são realizadas aqui

case MotionEvent.ACTION_MOVE: 
       if (!isRefreashing) 
         isRefreashing = true; 
       final float deltaY = ev.getRawY(); - mLastY; 
       mLastY = ev.getRawY(); 
       updateHeaderViewHeight(deltaY / 1.8f);//Reduzir a distância de movimento em uma certa proporção 
       updateHeight(); 
       break; 

Dentro deles, updateHeaderViewHeight e updateHeight são métodos para alterar a altura do HeaderView e a altura do Layout

 private void updateHeight() { 
    ViewGroup.LayoutParams lp = getLayoutParams(); 
    //Atualizar a altura da instância atual do layout para a altura do headerView mais a altura inicial do layout 
    //Se não atualizar o layout, comprimirá a altura do conteúdo, impossível manter a proporção 
    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) { 
      //Se não estiver em refresh e se a altura 
      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()); 
  } 

Ao atualizar a altura do Header, determina-se se foi atingido o distância de atualização puxando para baixo, no código acima, estou definindo que, ao atingir o dobro da altura inicial do mHeaderView, entra no estado de 'liberar atualização', se não for atingido, mantém o estado de 'atualizar arrastando para baixo'
O estado do HeaderView define um total de3Os respectivos são

public final static int STATE_NORMAL = 0;//下拉刷新 
 public final static int STATE_READY = 1;//释放刷新 
 public final static int STATE_REFRESHING = 2;//正在刷新 

O método de atualização da altura é o mesmo para headerView e layout, é adicionar a distância móvel à altura original e atribuir novamente ao headerView ou ao layout

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

Por fim, é o evento ACTION_UP, que é quando o dedo sai da tela, aqui precisamos decidir o estado final do headerView com base no estado atual do 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 e reset são métodos para resetar a altura do headerView e do 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; 
    } 
  } 

A maneira de implementação é a mesma. Baseado no estado, se estiver em estado de atualização, o headerView deve ser exibido normalmente e a altura deve ser a inicial, se estiver em NORMAL, ou seja, no estado de 'atualizar arrastando para baixo', diz que não foi acionado a atualização, ao resetar, o headerView deve ser ocultado, ou seja, a altura deve ser resetada para 0

Até aqui, a operação de atualização de arrastar para baixo também está praticamente completa, ainda é necessário adicionar uma interface de callback para notificação

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; 

Bom, até aqui, praticamente está completo, teste o efeito. Ei, notei um problema, por que este Layout não pode executar a atualização de arrastar para baixo quando usado dentro de um ListView aninhado! Pense bem, deve ser um problema de distribuição de eventos, precisa ser tratado!
Sobre o tratamento de interceptação de eventos, li o blog de distribuição de eventos do ViewGroup do grande mestre Hongyang e do Android-Ultra-Pull-To-A parte do código do Refresh, onde encontrei a solução:

@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("Encontrado 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); 
  } 

Dentre eles

if (space > 0 && !absListView.canScrollVertically(-1) && absListView.getFirstVisiblePosition() == 0)
Space é a distância móvel canScrollVertically() é usado para determinar se o ListView pode rolar na direção vertical, quando o parâmetro é negativo, representa rolagem para cima, quando é positivo, representa rolagem para baixo, e o último é a posição do primeiro item visível do ListView

Com o tratamento de interceptação de eventos acima, um ViewGroup que atende às necessidades mencionadas no início também está completo!

Aqui está o código-fonte do Layout e do HeaderView (usado diretamente pelo XlistView's HeaderView)

public class SRefreshLayout extends LinearLayout { 
  private SRefreshHeader mHeaderView; 
  private RelativeLayout mHeaderViewContent; 
  private boolean isRefreashing; 
  private float mLastY = -1;//Altura de início da pressão 
  private int mHeaderViewHeight;//Altura do conteúdo do headerView 
  private int mHeight;//Altura do layout 
  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(); 
  } 
  /** 
   * Obter a altura do headView 
   */ 
  private void getHeaderViewHeight() { 
    ViewTreeObserver vto2 = mHeaderViewContent.getViewTreeObserver(); 
    vto2.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { 
      @Override 
      public void onGlobalLayout() { 
        mHeaderViewHeight = mHeaderViewContent.getHeight(); 
        mHeaderViewContent.getViewTreeObserver().removeGlobalOnLayoutListener(this); 
      } 
    }); 
  } 
  /** 
   * Obter a altura da instância atual de 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("Encontrado 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);//Reduzir a distância de movimento em uma certa proporção 
        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(); 
    //Atualizar a altura da instância atual do layout para a altura do headerView mais a altura inicial do layout 
    //Se não atualizar o layout, comprimirá a altura do conteúdo, impossível manter a proporção 
    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) { 
      //Se não estiver em refresh e se a altura 
      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;//下拉刷新 
  public final static int STATE_READY = 1;//释放刷新 
  public final static int STATE_REFRESHING = 2;//正在刷新 
  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) { 
    // Em situação inicial, definir a altura da view de atualização下拉 para 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) {  // Mostrar progresso 
      mHeadArrowImage.clearAnimation(); 
      mHeadArrowImage.setVisibility(View.INVISIBLE); 
      mRefreshingProgress.setVisibility(View.VISIBLE); 
    } else {  // Mostrar imagem de seta 
      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("Recarregar"); 
        break; 
      case STATE_READY: 
        if (mState != STATE_READY) { 
          mHeadArrowImage.clearAnimation(); 
          mHeadArrowImage.startAnimation(mRotateUpAnim); 
          mHeadHintTxt.setText("solte para atualizar"); 
        } 
        break; 
      case STATE_REFRESHING: 
        mHeadHintTxt.setText("está atualizando"); 
        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(); 
  } 
} 

último é o arquivo de layout

<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="arraste para atualizar" /> 
      <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="última atualização" 
          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> 

Isso é tudo o que há no artigo. Esperamos que isso ajude no seu aprendizado e que você apoie o Tutorial Grito.

Declaração: O conteúdo deste artigo é de origem na internet, pertence ao respectivo proprietário, foi submetido e carregado voluntariamente pelos usuários da internet. Este site não possui direitos de propriedade, não foi editado manualmente e não assume responsabilidade por eventuais responsabilidades legais. Se você encontrar conteúdo suspeito de violação de direitos autorais, por favor, envie um e-mail para: notice#oldtoolbag.com (ao enviar e-mail, substitua # por @ para denunciar e forneça provas. Caso seja confirmada, o site deletará imediatamente o conteúdo suspeito de violação de direitos autorais.)

Você Também Pode Gostar