안드로이드 완두 꼬투리 모방 앱 리스트 추가 효과
효과도.gif
엊그저께 안드로이드폰(ps: 아이폰폰을 사용해 왔다)을 사서 앱 시장에 내려주려고 했는데 완두콩꼬투리를 좋아해서 완두콩꼬투리를 만들었어요. 완두콩꼬투리에서 앱을 다운받았는데 리스트가 화면에 들어오는 게 재미있어서 직접 흉내를 내려고 했어요.
사고의 방향
처음에 이 효과를 보았을 때 리스트 인터페이스와 상세 인터페이스가 Activity+dialog인지 아니면 두 개의 Activity인지 생각했다. 나중에 생각해 보니 상세 인터페이스에 데이터가 많은 것이 Activity일 가능성이 크지 않고 리스트 Activity일 수도 있고 상세 Activity일 수도 있다. 그러면 이 디자인에 대해 해결해야 할 문제가 많을 것이다.
이런 문제들을 가지고 저희가 하나하나 분석해서 해결해 보도록 하겠습니다.
이루어지다
int viewMarginTop = view.getTop() + getResources().getDimensionPixelOffset(R.dimen.bar_view_height);
Intent intent = new Intent(MainActivity.this, DetailActivity.class);
intent.putExtra("viewMarginTop", viewMarginTop);
intent.putExtra("imageId", (int) array[0]);
intent.putExtra("appName", (String) array[1]);startActivity(intent);
overridePendingTransition(0, 0);
view는 현재 클릭한 Item View,view입니다.getTop () 은 ItemView가 RecyclerView 맨 위에 있는 높이입니다. getResources ().dimensionPixel Offset(R.dimen.bar view height)는 RecyclerView 위에 있는 Title View의 높이입니다. 저는 상태 표시줄을 숨겼기 때문에 모든viewMarginTop은 현재 클릭한 Item View가 상태 표시줄 맨 위에 있는 높이입니다.overridePendingTransition(0,0)은 점프 애니메이션을 없애고 시각에 틈이 없도록 하는 것이다. 자세한 내용은Activity가 어떻게 표시되는지 아래에서 분석한다.
mSVRootLl.setContentInitMarginTop(mViewMarginTop);
//
public void setContentInitMarginTop(int marginTop) {
mContentMarginTop = marginTop;
requestLayout();
}
mViewMarginTop은 목록 인터페이스에서 전달되는 매개 변수이고 mSVRootLl은 ScrollView 아래의 루트LinearLayout입니다. 자세한 페이지는 굴러갈 수 있기 때문에 ScrollView가 필요합니다. 높이를 설정한 후에 RequestLayout 방법으로 레이아웃을 시작하고 onLayout 방법으로 레이아웃 높이를 설정하면 됩니다.
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
int contentTop = mContentMarginTop + mTouchMoveOffset;
mContentLL.layout(0, contentTop, mContentLlWidth, !mIsAnimation ? contentTop + mContentLlHeight : mInitBottom + mContentBottomOffset);
if(!mIsLayoutImageView) return;
int left = mMargin + mImageLeftOffset;
int top = mMargin + mImageTopOffset;
mIconImageView.layout(left, top, left + mIconImageViewWidth, top + mIconImageViewHeight);}
mContent MarginTop은 바로 방금 설정한 높이입니다. mTouch Move Offset 기본값은 0이고 mIs Animation 기본값은false입니다. mIs Layout ImageView 기본값은false입니다. 이런 매개 변수의 의미는 다음에 분석할 것입니다. 이렇게 하면 View의 높이는 목록 인터페이스에서 클릭한 Item View의 높이와 같습니다. 다음에 클릭한 Item View가 어떻게 상세 페이지로 바뀌었는지 분석합니다.
클릭한 Item View가 상세 정보Activity에 도착한 후에 Linear Layout 레이아웃이 되었다. 이 레이아웃은 세 부분으로 나뉜다. 타이틀 레이아웃, 중간 레이아웃,bottom 레이아웃이다. 기본 타이틀과bottom은 숨겨져 있기 때문에 기본적인 효과는 목록 인터페이스에서 Item View를 클릭한 효과이다. 이 View가 나타나면 바로 애니메이션을 통해 상세 인터페이스로 변한다.바로 위 애니메이션이 완성된 후의 효과입니다. 다음은 애니메이션의 논리 코드를 살펴보겠습니다.
private void startAnimation() {
ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 1).setDuration(400);
valueAnimator.setStartDelay(100);
valueAnimator.start();
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float ratio = (float) animation.getAnimatedValue();
//
int contentTopOffset = (int) (ratio * mContentTopOffsetNum);
//
int contentBottomOffset = (int) (ratio * mContentBottomOffsetNum);
//
int imageLeftOffset = (int) (ratio * mImageLeftOffsetNum);
//
int imageTopOffset = (int) (ratio * mImageTopOffsetNum);
mSVRootLl.setAllViewOffset(mViewMarginTop - contentTopOffset, contentBottomOffset, imageLeftOffset, imageTopOffset);
}
});
valueAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
mSVRootLl.setAnimationStatus(false);
mBottomLl.setVisibility(View.VISIBLE);
mTitleLl.setVisibility(View.VISIBLE);
}
});
}
onAnimationUpdate 방법에서 ratio에 따라 4개의 편이량을 계산하는 것을 볼 수 있다. 이 4개의 편이량은 무슨 소용이 있을까?클릭한 Item View가 애니메이션을 통해 상세 뷰로 바뀝니다.이 변화의 과정은 4가지 부분을 포함한다. 1: Item View의 상단 거리가 상단 거리가 점점 가까워진다. 2: Item View의 하단 거리가 하단 거리가 점점 가까워진다. 3: Item View의 그림은 천천히 가운데에 있다. 4: Item View의 그림은 천천히 아래로 가까워진다. (아래로 다가오지 않으면 애니메이션이 끝난 후에 title 레이아웃이 표시되고 그림은 아래로 점프하는 문제가 있다)
그러면 이 네 부분이 이동하는 총 거리를 ratio에 곱하면 애니메이션 실행 과정에서 매번 편이량이다. 그리고 편이량을 계속 설정해서 RequestLayout 방법을 호출하여 레이아웃을 해서 View가 애니메이션의 효과에 도달하도록 한다.위에서 말한 mIs Animation 이 필드는 이때true입니다. mIs Layout ImageView는true입니다. 애니메이션을 실행할 때만 이미지 컨트롤을 다시 레이아웃하고 애니메이션이 끝난 후에title 레이아웃과bottom 레이아웃을 표시합니다
@Override
public boolean onTouchEvent(MotionEvent event) {
boolean consumption = true;
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mInitY = event.getY();
getParent().requestDisallowInterceptTouchEvent(true);
break;
case MotionEvent.ACTION_MOVE:
float moveY = event.getY();
float yOffset = moveY - mInitY;
//
if((mParentScrollView.getScrollY() <= 0 && moveY >= mInitY) || mIsDrag) {
setTouchMoveOffset(yOffset);
mIsDrag = true;
consumption = true;
} else {
getParent().requestDisallowInterceptTouchEvent(false);
consumption = false;
}
break;
case MotionEvent.ACTION_UP:
mIsDrag = false;
boolean isUp = false;
int animationMoveOffset;
if(mContentLL.getTop() <= mCenterVisibleViewHeight / 2 + mTitleViewHeight) {
animationMoveOffset = mTouchMoveOffset;
isUp = true;
} else {
animationMoveOffset = mParentScrollView.getHeight() - mContentLL.getTop();
}
startAnimation(animationMoveOffset, isUp, mTouchMoveOffset);
break;
}
return consumption;
}
public void startAnimation(final int moveOffset, final boolean isUp, final int currentMoveOffset) {
int duration = moveOffset / mTouchSlop * 10;
if(duration <= 0) duration = 300;
ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 1).setDuration(duration);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float ratio = (float) animation.getAnimatedValue();
if(isUp)
mTouchMoveOffset = (int) (moveOffset * (1 - ratio));
else
mTouchMoveOffset = currentMoveOffset + (int) (moveOffset * ratio);
requestLayout();
updateBgColor(mTouchMoveOffset);
}
});
valueAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
if(!isUp && mOnCloseListener != null) mOnCloseListener.onClose();
}
});
valueAnimator.start();
}
애니메이션 실행 후 콜백 방법을 호출하여 상세 정보 닫기 Activity
mSVRootLl.setOnCloseListener(new SVRootLinearLayout.OnCloseListener() {
@Override
public void onClose() {
finish();
overridePendingTransition(0, 0);
}
});
public void updateBgColor(int offset) {
if(mOnUpdateBgColorListener != null) {
float ratio = BigDecimalUtils.divide(offset, mCenterVisibleViewHeight);
if(ratio > 1) ratio = 1;
if(ratio < 0) ratio = 0;
mOnUpdateBgColorListener.onUpdate(ratio);
}
}
이 방법의 mOn Update Bg Color Listener는 자세한 정보Activity 설정입니다. onUpdate 방법을 호출하여 배경의 색 투명도를 변경합니다.
mSVRootLl.setOnUpdateBgColorListener(new SVRootLinearLayout.OnUpdateBgColorListener() {
@Override
public void onUpdate(float ratio) {
mRootCDrawable.setAlpha((int) (mColorInitAlpha - mColorInitAlpha * ratio));
}
});
끝말
지금까지 프로젝트의 논리와 코드를 모두 분석했습니다. 문장을 통해 우리는 하나의 기능을 개발할 때 먼저 대체적인 사고방식을 가지고 이 기능을 어떻게 실현하고 싶은지 알 수 있습니다. 그리고 사고방식 중의 문제를 하나하나 해독하고 마지막에 연결하면 됩니다. 읽어주셔서 감사합니다. 원본을 보고 싶은 분은 github 주소로 이동하셔도 됩니다.https://github.com/chenpengfei88/WdjAppDetail스타도 환영합니다. 팔로우 본인도 환영합니다. 감사합니다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
다양한 언어의 JSONJSON은 Javascript 표기법을 사용하여 데이터 구조를 레이아웃하는 데이터 형식입니다. 그러나 Javascript가 코드에서 이러한 구조를 나타낼 수 있는 유일한 언어는 아닙니다. 저는 일반적으로 '객체'{}...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.