Android game details page (2)
Preface
Through the analysis in the previous article, I have basically understood the idea of the game details page. This article mainly aims to achieve the basic effect of the page.
Page Layout
Based on the analysis in the previous article, we know that the game details page is implemented through three different layers of layout overlays. To achieve this level structure, we need to use RelativeLayout.
The three View levels are: Introducing the head layout of the game introduction, details of the game details interface, and toolbar.
Introduce the head layout of the game introduction:
In the red circle, we will introduce the head layout of the game.
Layout_game_detail_head.xml
Displays the content layout of various game details
The yellow circle shows the layout of game dating content.
Layout_content.xml
ToolBar Layout
Layout_bar.xml
Homepage Layout
Activity_main.xml
These are some common view la S. By placing different la s in RelativeLayout, you can achieve complex interface effects.
Implementation of content interface movement
Observe the game content of dangle and find that there are three statuses of moving the content interface:
1. Status at the top
2. Intermediate status
3. Bottom status
When it is in the top state, in figure 1, the game introduction in the Red Circle is removed from the layout, and the tab is fixed under toobar.
2. toolba is completely transparent when it is in the intermediate state, and the UI of the game details is moved to the center, while the toolba is in the bottom state, as the layout of various game information is moved out of the interface, the game profile layout is fixed at the bottom of the screen.
In the process of moving, we need several parameters to define the location of several States in the moving layout:
mTopL = -mHeadH + mBarH; mCenterL = Util.dp2px(150); mBottomL = mScreenH - mStateBarH - mNBarH - mHeadH + mBarH;
MHeadH displays the View of the Game Information's header (the height of the View in the red circle)
Authorization + 2yA0KPHA + 0sa2r7K8vta1xMq1z9a0 + sLryOfPwjo8L3A + DQo8cHJlIGNsYXNzPQ = "brush: java;">
/** Use gestures to control the movement of GameContentView */class SimpleGestureAction extends GestureDetector. else {@ Override public boolean onScroll (MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {if (mRawY <= mTopL & distanceY> 0) {mRawY = mTopL; return true;} if (mRawY> = mBottomL & distanceY <0) {mRawY = mBottomL; return true;} mRawY-= distanceY; if (mRawY <mCenterL) {a + = DistanceY <0? -0.03: 0.03; if (a <0.0f) {a = 0.0f;} else if (a> 1.0f) {a = 1.0f ;}} else {a = 0.0f ;} if (mRawY <= mTopL) {mRawY = mTopL; a = 1.0f; mBarBg. setAlpha (a); mTemp. setAlpha (a);} mContent. setTranslationY (mRawY); if (mRawY> = mCenterL + mBarH) {rotationBanner (true);} return true ;}@ Override public boolean onTouchEvent (MotionEvent event) {switch (event. getAction () {case MotionEvent. ACTION_UP: if (mRawY <=-mStateBarH) {toTop ();} else if (-mStateBarH <mRawY & mRawY <= mCenterL + (mBarH <1 ))) {toCenter ();} else if (mCenterL + (mBarH <1) <= mRawY) {toBottom () ;}return true; default: if (0 <= a & a <= 1.0f) {mBarBg. setAlpha (a); mTemp. setAlpha (a);} mDetector. onTouchEvent (event); return super. onTouchEvent (event) ;}}/*** return to the top */private void toTop () {AnimatorSet set = new AnimatorSet (); ObjectAnimator animator = ObjectAnimator. ofFloat (mContent, "translationY", mRawY, mTopL); ObjectAnimator alpha = ObjectAnimator. ofFloat (mBarBg, "alpha", a, 1.0f); ObjectAnimator alpha1 = ObjectAnimator. ofFloat (mTemp, "alpha", a, 1.0f); set. setduration( 500); set. play (animator ). with (alpha ). with (alpha1); set. start (); mRawY = mTopL; // mCurrentState = STATE_TOP; a = 1.0f; mBarBg. setAlpha (a); mTemp. setAlpha (a); // showBottomBar (true);}/*** return to the center */private void toCenter () {ObjectAnimator animator = ObjectAnimator. ofFloat (mContent, "translationY", mRawY, mCenterL); animator. setDuration (500); animator. start (); mRawY = mCenterL; a = 0.0f; mBarBg. setAlpha (a); mTemp. setAlpha (a); mCurrentState = STATE_CENTER; rotationBanner (false); // showBottomBar (true);}/*** to the bottom */private void toBottom () {ObjectAnimator animator = ObjectAnimator. ofFloat (mContent, "translationY", mRawY, mBottomL); animator. setDuration (500); animator. start (); mRawY = mBottomL; a = 0.0f; mBarBg. setAlpha (a); mTemp. setAlpha (a); mCurrentState = STATE_BOTTOM; rotationBanner (true );
The above Code uses gestures to dynamically set the Y axis coordinates on the interface. There is nothing to say here, and it is a basic gesture operation.
At the same time, when moving to a certain position but not yet reaching the position we specified, we need to perform the rebound processing, and the rebound operation is implemented in the onTouchEvent overload method. When we detect that the finger is released, we can compare the current position with the defined interval to determine the status of the View.
* ToTop () is restored to the top state.
* The toCenter () is restored to the intermediate state.
* ToBottom () is restored to the bottom state.
Game rotation implementation
Moving has been implemented. Continue to observe the original interface. When the mobile layout moves from the intermediate state to the bottom state, the game in the bottom layer will rotate, restore the original state when it returns from the bottom to the middle.
The Code is as follows:
Displays the Fragment of the game.
Public class ScreenshotFragment extends BaseFragment {@ InjectView (R. id. img_banner) ImageView mBannerImg; private BannerEntity mEntity; private boolean isCanClick = false; private HandlerThread mHt; private RotationHandler mHandler; private handler () {} public static worker newInstance (BannerEntity entity) {ScreenshotFragment fragment = new ScreenshotFragment (); Bundle B = New Bundle (); B. putParcelable ("entity", entity); fragment. setArguments (B); return fragment;} @ Override protected void init (Bundle savedInstanceState) {mEntity = getArguments (). getParcelable ("entity"); mBannerImg. setScaleType (ImageView. scaleType. FIT_XY); setUpData (mEntity); mRootView. setOnClickListener (new View. onClickListener () {@ Override public void onClick (View v) {if (! IsCanClick) {return ;}}); mHt = new HandlerThread ("rotation_ht", Process. THREAD_PRIORITY_DEFAULT); mHt. start (); mHandler = new RotationHandler (mHt. getlogoff ();}/*** get ImageView */public ImageView getBannerImg () {return mBannerImg;}/*** set whether to click */public void setCanClick (boolean isCanClick) {this. isCanClick = isCanClick;}/*** set data ** @ param entity */private void setUpData (BannerEnt Ity entity) {mBannerImg. setTag (null); Glide. with (getContext ()). load (entity. getImgUrl ()). diskCacheStrategy (DiskCacheStrategy. ALL ). into (mBannerImg);}/*** set image */public void setDrawable (@ DrawableRes int drawable) {if (mBannerImg! = Null) {mBannerImg. setImageResource (drawable);}/*** update data */public void update (BannerEntity entity) {mEntity = entity; setUpData (entity );} /*** set Banner height ** @ param height */public void setBannerHeight (int height) {if (mBannerImg = null) {return;} ViewGroup. layoutParams lp = mBannerImg. getLayoutParams (); lp. height = height; mBannerImg. setLayoutParams (lp) ;}@ Override public void onD Estroy () {super. onDestroy (); if (mHt! = Null) {mHt. quit () ;}}/*** rotate the image ** @ param rotation determines whether to rotate */public void setRotation (boolean rotation) {setRotation (rotation, false );} /*** rotate the image ** @ param useAnim use an animation */public void setRotation (boolean rotation, boolean useAnim) {mHandler. obtainMessage (rotation? 0: 1, useAnim ). sendToTarget () ;}@ Override protected int setLayoutId () {return R. layout. fragment_banner;} private class RotationHandler extends Handler {public RotationHandler (Looper loler) {super (loation) ;}@ Override public void handleMessage (Message msg) {super. handleMessage (msg); if (mBannerImg = null) {return;} final int what = msg. what; final boolean useAnim = (boolean) msg. obj; mBannerImg. post (new Runnable () {@ Override public void run () {if (what = 0) {rotation (mBannerImg, useAnim);} else if (what = 1) {resumeRotation (mBannerImg, useAnim) ;}});}/*** rotate ** @ param img */private void rotation (ImageView img, boolean useAnim) {int w = Util. get1_wwidth (getActivity (), h = Util. getWindowHeight (getActivity (); int iw = img. getMeasuredWidth (), ih = img. getMeasuredHeight (); if (useAnim) {ObjectAnimator move = ObjectAnimator. ofFloat (img, "translationY", 0, (h-ih)/2f); move. setDuration (400); ObjectAnimator scaleX = ObjectAnimator. ofFloat (img, "scaleX", 1.0f, (float) h/iw); ObjectAnimator scaleY = ObjectAnimator. ofFloat (img, "scaleY", 1.0f, (float) w/ih); ObjectAnimator rotation = ObjectAnimator. ofFloat (img, "rotation", 0f, 90f); AnimatorSet set = new AnimatorSet (); set. play (scaleX ). with (scaleY ). with (rotation ). with (move); set. setduration( 600); set. start ();} else {img. setTranslationY (h-ih)/2f); img. setScaleX (float) h/iw); img. setScaleY (float) w/ih); img. setRotation (90f);}/*** restore ** @ param img */private void resumeRotation (ImageView img, boolean useAnim) {int w = Util. get1_wwidth (getActivity (), h = Util. getWindowHeight (getActivity (); int iw = img. getMeasuredWidth (), ih = img. getMeasuredHeight (); if (useAnim) {ObjectAnimator move = ObjectAnimator. ofFloat (img, "translationY", (h-ih)/2f, 0); move. setDuration (400); ObjectAnimator scaleX = ObjectAnimator. ofFloat (img, "scaleX", (float) h/iw, 1.0f); ObjectAnimator scaleY = ObjectAnimator. ofFloat (img, "scaleY", (float) w/ih, 1.0f); ObjectAnimator rotation = ObjectAnimator. ofFloat (img, "rotation", 90f, 0f); AnimatorSet set = new AnimatorSet (); set. play (scaleX ). with (scaleY ). with (rotation ). with (move); set. setduration( 600); set. start ();} else {img. setTranslationY (0f); img. setScaleX (1.0f); img. setScaleY (1.0f); img. setRotation (0f );}}}}
ScreenshotFragment is used to display the game interface. When you move a game, you need to perform the following operations: Rotate-> move to the middle-> zoom in. These three operations are animated by attributes, it is easy to implement the animation effect. Note that during the enlargement process, we need to change the width and height of the ImageView so that the ImagView can fully display the enlarged image, at the same time, to ensure the security of UI updates, an asynchronous handler is required to implement its update operations. Here I use a lightweight HandlerThread to implement this asynchronous UI update operation.
/*** Initialize the game ViewPager ** @ return */private void setupGameShotVp (final ViewPager viewPager) {SimpleViewPagerAdapter adapter = new SimpleViewPagerAdapter (getsuppfrfragmentmanager (); List
Data = getBannerData (); for (BannerEntity entity: data) {adapter. addFrag (ScreenshotFragment. newInstance (entity), "");} viewPager. setAdapter (adapter); viewPager. setOffscreenPageLimit (data. size (); mIndicator. setViewPager (viewPager); // mIndicator. onPageSelected (0); viewPager. addOnPageChangeListener (new ViewPager. onPageChangeListener () {@ Override public void onPageScrolled (int position, float posi TionOffset, int positionOffsetPixels) {mShotVpPosition = position ;}@ Override public void onPageSelected (int position) {}@ Override public void onPageScrollStateChanged (int state ){}}); // set the Banner image height to new Handler (). post (new Runnable () {@ Override public void run () {viewPager. post (new Runnable () {@ Override public void run () {SimpleViewPagerAdapter adapter = (SimpleViewPagerAdapter) mImgVP. g EtAdapter (); int h = (int) getResources (). getDimension (R. dimen. game_detail_head_img_vp_height); for (int I = 0, count = adapter. getCount (); I <count; I ++) {ScreenshotFragment fragment = (ScreenshotFragment) adapter. getItem (I); if (fragment! = Null) {fragment. setBannerHeight (h );}}}});}});}
The above is the game initialization code. In the initial position, the height of the ViewPage needs to be fixed. After rotation, you need to set the height of the ViewPage to the height of the screen. In this way, in order to ensure that the game can be fully displayed.
This is basically the result we have achieved. It is getting closer and closer to the game details page of dangle, but it cannot respond to events, the next step is to handle the most challenging sliding conflicts and event distribution. I'm a little excited .....