Android RefreshLayout 드 롭 다운 레이아웃 새로 고침 실현
25703 단어 RefreshLayout드 롭 다운 리 셋
그래서 XlistView 의 소스 코드 를 꺼 내 서 조금씩 보 았 고 XLisview 소스 코드 를 대충 이해 한 다음 에 자신 이 시작 하기 로 결정 했다.
헤드 뷰 는 편리 하 게 X ListView 의 헤드 뷰 를 사용 하여 많은 것 을 절약 했다.)
드 롭 다운 리 셋,드 롭 다운 리 셋 은 드 롭 다운 기능 을 먼저 실현 하 는 것 이 분명 합 니 다.처음에 저 는 extends ScrollView 를 통 해 이 루어 지 려 고 했 습 니 다.기 존의 스크롤 효과 가 있 기 때 문 입 니 다.그런데 실제 적 으로 두 가지 이유 로 포 기 했 습 니 다.
1.ScrollView 아래 에 키 컨트롤 View 만 있 을 수 있 습 니 다. Scroll 다음 에 ViewGroup 을 추가 한 다음 에 headView 동 태 를 앞의 ViewGroup 에 추가 합 니 다.하지만 저 는 studio 의 시각 화 미리 보기 에 익숙 합 니 다.직관 적 이지 않 습 니 다!
2、 ScrollView 에 ListView 가 내장 되 어 있 을 때 발생 합 니 다. 충돌,ListView 를 다시 써 야 합 니 다.그래서 생각 을 바 꾸 는 것 을 포기 합 니 다!
위의 이유 1:스크롤 뷰 의 그룹 뷰 에 헤드 뷰 를 동적 으로 추가 합 니 다.스크롤 뷰 를 다시 쓰 는 onView Added()방법 으로 초기 화 할 때 분 석 된 헤드 뷰 를 하위 그룹 뷰 에 추가 할 수 있 습 니 다.
@Override
public void onViewAdded(View child) {
super.onViewAdded(child);
// headView , Vertical LinearLayout
LinearLayout linearLayout = (LinearLayout) getChildAt(0);
linearLayout.addView(view, 0);
}
사고방식 을 바 꾸 고 extends LinearLayout 를 통 해 실현 합 시다!먼저 준비 작업 을 하려 면 HeaderView 와 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);
이것 은 header View 내용 영역 view 를 분석 하 는 것 입 니 다.잠시 후에 이 view 의 높이 를 얻 으 려 면 위의 mHeader View 를 사용 하지 않 고 높이 를 얻 는 이 유 를 물 을 것 입 니 다.구조 방법 에 들 어가 면 다음 과 같은 코드 를 볼 수 있 습 니 다.
// , view 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);
}
});
}
준비 작업 이 완성 되 었 으 니,다음은 하향 작업 이 될 것 이다.여기까지 온 터치 이벤트()방법 이 떠 올 랐 을 거 예요.그 렇 죠!지금부터 이곳 에서 공 사 를 시작 하 겠 습 니 다.
드 롭 다운 을 이 루 는 데 는 총 세 가지 과정 이 있 습 니 다.
ACTION_UP→ACTION_MOVE→ACTION_UP
ACTION 에서UP 이벤트 에서 손가락 을 눌 렀 을 때 우리 가 해 야 할 일 은 눌 렀 을 때의 좌 표를 기록 하 는 것 입 니 다.
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
//
mLastY = ev.getRawY();// Y
break;
그리고 ACTIONMOVE 이벤트 입 니 다.여기 가 가장 중요 합 니 다.드 롭 다운 시 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;
안에 있 는 updateHeaderView Height 와 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 의 상 태 는 총 3 개가 설정 되 어 있 습 니 다.
public final static int STATE_NORMAL = 0;//
public final static int STATE_READY = 1;//
public final static int STATE_REFRESHING = 2;//
높이 를 업데이트 하 는 방법 은 header View 와 layot 가 같 습 니 다.바로 원래 높이 에 이동 거 리 를 더 해 header View 나 layot 에 다시 부여 하 는 것 입 니 다.mHeaderView.setVisiableHeight((int) space
+ mHeaderView.getVisiableHeight());
마지막 으로 ACTIONUP 사건 은 손가락 이 화면 을 떠 날 때 입 니 다.여기 서 header View 의 현재 상태 에 따라 header View 의 최종 상 태 를 결정 해 야 합 니 다!
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 높이 와 layot 높이 를 리 셋 하 는 방법 입 니 다.
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;
}
}
실현 방식 도 마찬가지다.상태 에 따라 판단 할 때 만약 에 새로 고침 중이 라면 header View 는 정상적으로 표시 되 어야 하고 높이 는 초기 높이 이 며 NORMAL,즉'드 롭 다운 새로 고침'상태 에 있 으 면 새로 고침 을 촉발 하지 않 고 리 셋 할 때 header View 는 숨겨 져 야 합 니 다.즉,고도 리 셋 은 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 는 드 롭 다운 리 셋 을 실행 할 수 없 습 니까?곰 곰 이 생각해 보면 사건 배포 의 문제 일 것 이 고 사건 의 차단 도 처리 해 야 한다!사건 차단 처리 에 대해 홍 양 대신 이 쓴 view group 사건 이 배포 한 블 로그 와 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 즉 이동 하 는 거리 canScroll Vertically()는 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;//
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) {
// , view 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>
이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.