Android自定义布局仿京东详情页,上下翻页

本文介绍如何在Android中通过自定义布局来实现类似京东商品详情页的上下翻页效果,包括项目的整体结构和关键实现步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

效果图


项目结构:


TBLayout中
package com.taobao.detailview;

import android.annotation.SuppressLint;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.Scroller;

//com.taobao.detailview.TBLayout
public class TBLayout extends LinearLayout {
    private OnPullListener pullListener;
    private OnPageChangedListener ctListener;

    public void setOnPullListener(OnPullListener listener) {
        this.pullListener = listener;
    }

    public void setOnContentChangeListener(OnPageChangedListener ler) {
        this.ctListener = ler;
    }

    private View mHeader;
    private View mFooter;
    private Scroller scroller;
    private int mTouchSlop = 0;

    private int mLastY;
    private int mLastInterceptY;
    private int mHeaderHeight;

    public final static int SCREEN_HEADER = 11;
    public final static int SCREEN_FOOTER = 12;

    private int screen = SCREEN_HEADER;

    @SuppressLint("NewApi")
    public TBLayout(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init();
    }

    public TBLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public TBLayout(Context context) {
        super(context);
        init();
    }

    private void init() {
        scroller = new Scroller(getContext());
        mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
    }

    @Override
    public void onWindowFocusChanged(boolean hasWindowFocus) {
        super.onWindowFocusChanged(hasWindowFocus);
        if (hasWindowFocus && mHeader == null && mFooter == null) {
            initData();
        }
    }

    private void initData() {
        mHeader = findViewById(R.id.header);
        mFooter = findViewById(R.id.footer);
        ViewGroup.LayoutParams lps = mFooter.getLayoutParams();
        mHeaderHeight = mHeader.getMeasuredHeight();
        lps.height = mHeaderHeight;
        mFooter.setLayoutParams(lps);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        boolean result = false;
        final int y = (int) ev.getY();
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mLastInterceptY = mLastY = y;
                break;
            case MotionEvent.ACTION_MOVE:
                int dy = y - mLastInterceptY;
                if (dy > mTouchSlop && screen == SCREEN_FOOTER) {// pull down
                    result = (pullListener != null && pullListener
                            .footerHeadReached(ev));
                } else if (dy < -mTouchSlop && screen == SCREEN_HEADER) { // pull up
                    result = (pullListener != null && pullListener
                            .headerFootReached(ev));
                }
                break;
            case MotionEvent.ACTION_UP:
                mLastInterceptY = 0;
                break;
        }
        return result;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        final int y = (int) event.getY();
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                break;
            case MotionEvent.ACTION_MOVE:
                int dy = y - mLastY;
                switch (screen) {
                    case SCREEN_HEADER:
                        int sy = -dy;
                        if (sy < 0) {
                            sy = 0;
                        } else if (sy > getHeight()) {
                            sy = getHeight();
                        }
                        scrollTo(0, sy);
                        break;
                    case SCREEN_FOOTER:
                        if (dy > 0) {
                            scrollTo(0, mHeaderHeight - dy);
                        } else { // dy < 0
                        }
                        break;
                }
                break;
            case MotionEvent.ACTION_UP:
                int t = 0;
                switch (screen) {
                    case SCREEN_HEADER:
                        t = mHeaderHeight / 4;
                        break;
                    case SCREEN_FOOTER:
                        t = mHeaderHeight * 3 / 4;
                        break;
                }
                int sy = getScrollY();
                if (sy > t) { // scroll to footer
                    scroller.startScroll(0, sy, 0, mHeaderHeight - sy, 150);
                    screen = SCREEN_FOOTER;
                    if (ctListener != null) {
                        ctListener.onPageChanged(SCREEN_FOOTER);
                    }
                    invalidate();
                } else { // scroll to header
                    scroller.startScroll(0, sy, 0, -sy, 150);
                    screen = SCREEN_HEADER;
                    if (ctListener != null) {
                        ctListener.onPageChanged(SCREEN_HEADER);
                    }
                    invalidate();
                }
                mLastY = 0;
                break;
        }
        return true;
    }

    @Override
    public void computeScroll() {
        if (scroller.computeScrollOffset()) {
            scrollTo(scroller.getCurrX(), scroller.getCurrY());
            postInvalidate();
        }
    }

    public interface OnPullListener {
        // 到达Header(第一页)的底部
        public boolean headerFootReached(MotionEvent event);

        // 到达Footer(第二页)的顶部
        public boolean footerHeadReached(MotionEvent event);
    }

    public interface OnPageChangedListener {
        // 页面改变事件
        public void onPageChanged(int stub);
    }
}
Activity_ScrollView中
package com.taobao.detailview;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MotionEvent;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.TextView;
import android.widget.Toast;

import com.taobao.detailview.TBLayout.OnPageChangedListener;
import com.taobao.detailview.TBLayout.OnPullListener;

/**
 * Header's root is ScrollView , Footer's root is ScrollView
 * 
 * @author zy
 * 
 */
@SuppressWarnings("unused")
public class Activity_ScrollView extends Activity  {
//	滑动翻页的外层自定义控件
	private TBLayout tblayout;
//  第一页的ScrollView
	private ScrollView header;
//	和第二页的ScrollView
    private ScrollView	footer;
//	第一页ScrollView的内容
	private LinearLayout mHeaderContent;
//	第二页ScrollView的内容
	private LinearLayout mFooterContent;



	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_scrollview);
		tblayout = (TBLayout) findViewById(R.id.tblayout);
		header = (ScrollView) findViewById(R.id.header);
		footer = (ScrollView) findViewById(R.id.footer);
		mHeaderContent = (LinearLayout) header.getChildAt(0);
		mFooterContent = (LinearLayout) footer.getChildAt(0);

		tblayout.setOnPullListener(new OnPullListener() {
			@Override
			public boolean headerFootReached(MotionEvent event) {
				if (header.getScrollY() + header.getHeight() >= mHeaderContent
						.getHeight()) {
					return true;
				}
				return false;
			}

			@Override
			public boolean footerHeadReached(MotionEvent event) {
				if (footer.getScrollY() == 0) {
					return true;
				}
				return false;
			}
		});
//		翻页
		tblayout.setOnContentChangeListener(new OnPageChangedListener() {
			@Override
			public void onPageChanged(int stub) {
				switch (stub) {
					case TBLayout.SCREEN_HEADER:
						Log.d("tag", "SCREEN_HEADER");
						Toast.makeText(Activity_ScrollView.this,"第一页",Toast.LENGTH_SHORT).show();
						break;
					case TBLayout.SCREEN_FOOTER:
						Log.d("tag", "SCREEN_FOOTER");
						Toast.makeText(Activity_ScrollView.this,"第2页",Toast.LENGTH_SHORT).show();
						break;
				}
			}
		});


	}



}
activity_scrollview.xml中
<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    xmlns:android="http://schemas.android.com/apk/res/android">
    <com.taobao.detailview.TBLayout
        android:id="@+id/tblayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#646464"
        android:orientation="vertical" >

        <ScrollView
            android:id="@id/header"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="#449cfd" >
            <include layout="@layout/page1_scrollview" />
        </ScrollView>

        <ScrollView
            android:id="@id/footer"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="#f97171" >

            <include layout="@layout/page2_scrollview" />
        </ScrollView>

    </com.taobao.detailview.TBLayout>
</LinearLayout>
page1_scrollview.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical" >

    <TextView
        android:id="@+id/tvPage1"
        android:layout_width="match_parent"
        android:layout_height="200.0dip"
        android:gravity="center"
        android:text="我是第一页"
        android:textColor="#ff0000"
        android:textSize="18sp" />

    <TextView
        android:layout_width="match_parent"
        android:layout_height="200.0dip"
        android:gravity="center"
        android:text="我是第一页"
        android:textColor="#ff0000"
        android:textSize="18sp" />

    <TextView
        android:layout_width="match_parent"
        android:layout_height="200.0dip"
        android:gravity="center"
        android:text="我是第一页"
        android:textColor="#ff0000"
        android:textSize="18sp" />

    <TextView
        android:layout_width="match_parent"
        android:layout_height="200.0dip"
        android:gravity="center"
        android:text="我是第一页"
        android:textColor="#ff0000"
        android:textSize="18sp" />

    <TextView
        android:layout_width="match_parent"
        android:layout_height="200.0dip"
        android:gravity="center"
        android:text="我是第一页"
        android:textColor="#ff0000"
        android:textSize="18sp" />

    <TextView
        android:layout_width="match_parent"
        android:layout_height="200.0dip"
        android:gravity="center"
        android:text="我是第一页"
        android:textColor="#ff0000"
        android:textSize="18sp" />

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:padding="10.0dip"
        android:text="--继续拖动,查看图文详情--"
        android:textSize="15sp" />

</LinearLayout>
page2_scrollview.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical" >

    <!--滑动后下面的部分,包含选项卡-->
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <!--头部-->
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:orientation="horizontal"
            android:background="@android:color/white"
            >
            <!--详情页-->
            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="50dp"
                android:orientation="horizontal"
                android:layout_marginLeft="20dp"
                android:layout_marginRight="20dp"

                >
                <!--3个选项卡-->
                <!--选项卡1-->
                <LinearLayout
                    android:id="@+id/llTuWen"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:orientation="vertical"
                    android:layout_weight="1"
                    >
                    <TextView
                        android:id="@+id/tvTuWen"
                        android:layout_width="wrap_content"
                        android:layout_height="48dp"
                        android:text="图文详情"
                        android:gravity="center"
                        android:layout_gravity="center"
                        android:textSize="14sp"

                        />
                    <TextView
                        android:id="@+id/tvTag1"
                        android:layout_width="72dp"
                        android:layout_height="2dp"

                        android:gravity="center"
                        android:layout_gravity="center"
                        />
                </LinearLayout>
                <LinearLayout
                    android:id="@+id/llTaoCan"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:orientation="vertical"
                    android:layout_weight="1"
                    >
                    <TextView
                        android:layout_width="wrap_content"
                        android:layout_height="48dp"
                        android:text="套餐说明"
                        android:gravity="center"
                        android:layout_gravity="center"
                        android:textSize="14sp"
                        android:textColor="#666666"
                        />
                    <TextView
                        android:id="@+id/tvTag2_1"
                        android:layout_width="72dp"
                        android:layout_height="2dp"

                        android:gravity="center"
                        android:layout_gravity="center"
                        android:visibility="gone"
                        />
                </LinearLayout>
                <LinearLayout
                    android:id="@+id/llParameter"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:orientation="vertical"
                    android:layout_weight="1"
                    >
                    <TextView
                        android:id="@+id/tvCanShu"
                        android:layout_width="wrap_content"
                        android:layout_height="48dp"
                        android:text="规格参数"
                        android:gravity="center"
                        android:layout_gravity="center"
                        android:textSize="14sp"
                        android:textColor="#666666"
                        />
                    <TextView
                        android:id="@+id/tvTag2"
                        android:layout_width="72dp"
                        android:layout_height="2dp"

                        android:gravity="center"
                        android:layout_gravity="center"
                        android:visibility="gone"
                        />
                </LinearLayout>
                <LinearLayout
                    android:id="@+id/llSellAfter"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:orientation="vertical"
                    android:layout_weight="1"
                    >
                    <TextView
                        android:id="@+id/tvShouHou"
                        android:layout_width="wrap_content"
                        android:layout_height="48dp"
                        android:text="包装售后"
                        android:gravity="center"
                        android:layout_gravity="center"
                        android:textSize="14sp"
                        android:textColor="#666666"
                        />
                    <TextView
                        android:id="@+id/tvTag3"
                        android:layout_width="72dp"
                        android:layout_height="2dp"

                        android:gravity="center"
                        android:layout_gravity="center"
                        android:visibility="gone"
                        />
                </LinearLayout>


            </LinearLayout>
        </LinearLayout>


        <!--下面部分,装入不同的布局,在外面包一层,滑动布局-->

        <ScrollView
            android:layout_width="match_parent"
            android:layout_height="match_parent">
            <LinearLayout
                android:id="@+id/llContainerfragment"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:orientation="vertical"
                >
            </LinearLayout>
        </ScrollView>
    </RelativeLayout>

</LinearLayout>
id.xml
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:android="http://schemas.android.com/apk/res/android">

    <item name="header" type="id">12</item>
    <item name="footer" type="id">34</item>

</resources>
源码下载:
http://download.youkuaiyun.com/detail/zhaihaohao1/9815988






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值