写在前面

通过上一篇文章的分析,基本已经了解当乐游戏详情页面的思想思路了,本篇文章主要是实现页面的基本效果。
android 仿当乐游戏详情页面(一)

页面布局

通过上一篇文章分析,已经知道,当乐游戏详情页是通过3个不同层次的布局进行叠加来实现的,为了实现这种层次结构,需要用到RelativeLayout 。
这3个View层次如图所示,分别为:介绍游戏简介的头布局、介绍游戏详情的详情界面、还有toolbar。

介绍游戏简介的头布局:


如图所示,红色圈圈里面的便是介绍这个游戏的头布局。

layout_game_detail_head.xml

<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/head" android:layout_width="match_parent" android:layout_height="@dimen/game_detail_head_height">    <View  android:id="@+id/temp" android:layout_width="match_parent" android:layout_height="30dp" android:background="@color/transparent" />    <me.relex.circleindicator.CircleIndicator  android:id="@+id/indicator" android:layout_width="match_parent" android:layout_height="@dimen/game_detail_head_indicator_height" android:gravity="center" />    <RelativeLayout  android:id="@+id/head_content" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_below="@+id/temp" android:background="@color/white">        <TextView  android:id="@+id/name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="130dp" android:layout_marginTop="8dp" android:text="游戏名" android:textColor="@color/text_black_color" android:textSize="@dimen/text_larger" />        <!--<com.lyy.ui.widget.StarBar-->            <!--android:id="@+id/star_bar"-->            <!--android:layout_width="wrap_content"-->            <!--android:layout_height="wrap_content"-->            <!--android:layout_alignLeft="@+id/name"-->            <!--android:layout_below="@+id/name"-->            <!--android:layout_marginTop="2dp" />-->        <TextView  android:id="@+id/detail" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignLeft="@+id/name" android:layout_below="@+id/name" android:text="角色扮演" android:textColor="@color/text_gray_color" android:textSize="@dimen/text_medium" />        <TextView  android:id="@+id/feature" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignLeft="@+id/name" android:layout_below="@+id/detail" android:text="特性111111" android:textColor="@color/text_gray_color" android:textSize="@dimen/text_medium" />        <View  android:layout_width="match_parent" android:layout_height="1dp" android:layout_alignParentBottom="true" android:background="@color/line_color" />    </RelativeLayout>    <View  android:layout_width="100dp" android:layout_height="50dp" android:layout_alignBottom="@+id/icon" android:layout_alignParentRight="true" android:visibility="gone" />    <ImageView  android:id="@+id/icon" android:layout_width="100dp" android:layout_height="100dp" android:layout_marginLeft="16dp" android:layout_marginTop="10dp" android:src="@mipmap/default_icon" /></RelativeLayout>

展示游戏各种详情的内容布局


如图所示,黄色圈圈里面的是展示游戏相亲的内容布局。

layout_content.xml

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/content" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_below="@+id/game_detail_bar" android:fitsSystemWindows="true" android:orientation="vertical">    <include  layout="@layout/layout_game_detail_head" android:layout_width="match_parent" android:layout_height="@dimen/game_detail_head_height" />    <android.support.design.widget.TabLayout  android:id="@+id/tab" style="@style/TabLayoutStyle" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@color/white" />    <View  android:layout_width="match_parent" android:layout_height="1dp" android:background="@color/line_color" />    <android.support.v4.view.ViewPager  android:id="@+id/content_vp" android:layout_width="match_parent" android:layout_height="match_parent" /></LinearLayout>

ToolBar的布局

layout_bar.xml

<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/game_detail_bar" android:layout_width="match_parent" android:layout_height="@dimen/tool_bar_height" android:fitsSystemWindows="true">    <View  android:id="@+id/bar_bg" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@color/colorPrimary" />    <TextView  android:id="@+id/back" android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_centerVertical="true" android:gravity="center" android:onClick="onClick" android:paddingLeft="-5dp" android:paddingRight="8dp" android:text="test" android:textColor="#fff" android:textSize="16sp" />    <ImageView  android:id="@+id/download_manager" style="@style/BarImgStyle" android:layout_alignParentRight="true" android:onClick="onClick" android:scaleType="fitCenter" android:src="@mipmap/icon_download" /></RelativeLayout>

主页面的布局

activity_main.xml

<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical">    <android.support.v4.view.ViewPager  android:id="@+id/img_vp" android:layout_width="match_parent" android:layout_height="match_parent"/>    <include layout="@layout/layout_content"/>    <View  android:id="@+id/state_bar_temp" android:layout_width="match_parent" android:layout_height="@dimen/state_bar_height" android:background="@color/colorPrimary"/>    <include  layout="@layout/layout_bar" android:layout_width="match_parent" android:layout_height="@dimen/tool_bar_height"/></RelativeLayout>

这些都是一些常规的视图布局,通过在RelativeLayout里面对各个布局进行不同层次的摆放以达到实现复杂界面的效果。

内容界面移动的实现

观察当乐的游戏内容介绍,发现内容界面的移动有如下三种状态:
1、处于顶部的状态

2、中间状态

3、底部状态

处于顶部状态时,图一中,红色圈圈部分的游戏简介被移出布局之外,并且tab被固定在toobar下面。
如图二所示,当处于中间状态时,toolba完全透明,并且介意游戏各种详情的界面移动到中间,而当其处于底部状态时,由于展示游戏各种信息的布局被移出来界面之外,此时,游戏简介布局被固定在屏幕底部。

在移动的过程中,我们需要几个参数来定义移动布局几个状态所处的位置:

 mTopL = -mHeadH + mBarH; mCenterL = Util.dp2px(150); mBottomL = mScreenH - mStateBarH - mNBarH - mHeadH + mBarH;
  • mHeadH 展示游戏信息的头部的View(下图红色圈中的View的高度)
  • mScreenH 屏幕的高度
  • mStateBarH 状态栏的高度
  • mNBarH 导航栏的高度
  • mBarH toolbar或tabbar的高度

移动布局的实现代码如下:

     /**通过手势控制GameContentView的移动*/     class SimpleGestureAction extends GestureDetector.SimpleOnGestureListener {        @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);        }    }    /** * 回到顶部 */    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);    }    /** * 回到中间 */    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);    }    /** * 到底部 */    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);

上面的代码是通过手势对界面Y轴坐标进行动态设置来实现的,这里面也没啥好说的,都是基本的手势操作。
同时,当移动到一定位置,但是还没有到达我们指定的位置时,需要对其进行回弹处理,而回弹操作是在onTouchEvent的重载方法实现的。当检测到手指松开时,通过当前所处的位置和我们定义的区间进行对比,来判断View应该回弹到哪个状态。
* toTop()恢复到顶部状态
* toCenter() 恢复到中间状态
* toBottom() 恢复到底部状态

游戏截图旋转实现

移动已经实现了,继续观察当初的界面,当可移动的布局从中间状态移动到底部状态时,处于最底层的游戏截图会进行旋转,当其从底部返回到中间时,恢复原始状态。
代码如下:
用于显示游戏截图的Fragment。

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 ScreenshotFragment() {    }    public static ScreenshotFragment 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.getLooper());    }    /** * 获取ImageView */    public ImageView getBannerImg() {        return mBannerImg;    }    /** * 设置能否点击 */    public void setCanClick(boolean isCanClick) {        this.isCanClick = isCanClick;    }    /** * 设置数据 * * @param entity */    private void setUpData(BannerEntity entity) {        mBannerImg.setTag(null);        Glide.with(getContext()).load(entity.getImgUrl())                .diskCacheStrategy(DiskCacheStrategy.ALL)                .into(mBannerImg);    }    /** * 设置图片 */    public void setDrawable(@DrawableRes int drawable) {        if (mBannerImg != null) {            mBannerImg.setImageResource(drawable);        }    }    /** * 更新数据 */    public void update(BannerEntity entity) {        mEntity = entity;        setUpData(entity);    }    /** * 设置Banner高度 * * @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 onDestroy() {        super.onDestroy();        if (mHt != null) {            mHt.quit();        }    }    /** * 对图片进行旋转 * * @param rotation 是否旋转 */    public void setRotation(boolean rotation) {        setRotation(rotation, false);    }    /** * 对图片进行旋转 * * @param useAnim 使用动画 */    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 looper) {            super(looper);        }        @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);                    }                }            });        }        /** * 旋转 * * @param img */        private void rotation(ImageView img, boolean useAnim) {            int w = Util.getWindowWidth(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);            }        }        /** * 恢复 * * @param img */        private void resumeRotation(ImageView img, boolean useAnim) {            int w = Util.getWindowWidth(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 是用来展示游戏截图的界面,当进行移动时,需要执行以下操作:旋转–>移动到中间–>进行放大,这三个操作,通过属性动画,便能很容易实现其动画效果,需要注意的是,在进行放大的过程中,我们需要改变ImageView的宽和高,让ImagView能完整展示放大后的图片,同时为来保证UI更新的安全性,需要使用一个异步handler来实现其更新操作,我在这里是使用轻量级的HandlerThread来实现这异步更新UI的操作。

/** * 初始化游戏截图ViewPager * * @return */    private void setupGameShotVp(final ViewPager viewPager) {        SimpleViewPagerAdapter adapter = new SimpleViewPagerAdapter(getSupportFragmentManager());        List<BannerEntity>     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 positionOffset, int positionOffsetPixels) {                mShotVpPosition = position;            }            @Override            public void onPageSelected(int position) {            }            @Override            public void onPageScrollStateChanged(int state) {            }        });        //设置Banner图片高度        new Handler().post(new Runnable() {            @Override            public void run() {                viewPager.post(new Runnable() {                    @Override                    public void run() {                        SimpleViewPagerAdapter adapter = (SimpleViewPagerAdapter) mImgVP.getAdapter();                        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);                            }                        }                    }                });            }        });    }

上面是初始化游戏截图的代码,在初始位置时,需要固定ViewPage的高度,旋转完毕后,需要将ViewPage高度设置为屏幕的高度,这样,才能保证游戏截图能被完全显示。


这是我们基本完成来的效果,已经越来越接近于当乐的游戏详情页面来,但是还不能进行相应的事件响应,接下来就是处理最具挑战的滑动冲突和事件分发了。想想我还是有点小激动呢…..

更多相关文章

  1. Activity布局初步 RelativieLayout相对布局
  2. Android中去掉或更改标题栏TitleBar,theme的更改
  3. android TextView 显示不全的问题解决,此问题是设置了maxLines和e
  4. Android中线程和Handle
  5. android 点击链接进入APP二级页面,不管APP是否处于打开状态
  6. Android中源码Launcher主屏幕程序排列详解【安卓Launcher进化一
  7. 认识AndEngine选自Android(安卓)2D游戏引擎AndEngine快速入门教
  8. Android之ToolBar和自定义ToolBar实现沉浸式状态栏
  9. [置顶] 我的Android进阶之旅------>Android中使用HTML作布局文件

随机推荐

  1. Android:解析 SurfaceView & TextureView
  2. Android 8.0 SystemUI下拉状态栏快捷开关
  3. Android开发者官网:Android 4.4发布10个月
  4. 第2步:第一个“Hello,world!”之Android(
  5. Android移植到mini2440(进行中)
  6. Android开发人员资料大全(开发人员必看)
  7. Android(安卓)Studio添加Parcelable序列
  8. android的binder机制研究
  9. Android中的数据持久化技术
  10. Android之个性化ListView实现