編輯:關於Android編程
公司項目需求,需要一個類似淘寶詳情一樣的頁面,但又有所不同,於是自己寫了一個。本文只是提供一種思路,控件的普適性還不夠,希望讀者能自行修改。
老規矩,先上一個項目原圖吧:


本文只是上圖右邊部分效果。demo也只有右邊部分。不過這個控件的代碼是一樣的。
直接貼代碼
TwoPageLayout.java:
/**
* Created by paulz on 2016/9/21.
*/
public class TwoPageLayout extends ViewGroup {
Context context;
View pageOne;
View pageTwo;
private int mTouchSlop;
private int mPageChangeLimit;
private int currentPage;
private boolean isIntercept;
OnPageChangeListener mOnPageChangeListener;
public OverScroller scroller;
public TwoPageLayout(Context context) {
super(context);
init(context);
}
public TwoPageLayout(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public TwoPageLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
private void init(Context context){
this.context=context;
final ViewConfiguration vc = ViewConfiguration.get(context);
mTouchSlop=vc.getScaledTouchSlop();
//滑動翻頁的臨界值
mPageChangeLimit=150;
scroller=new OverScroller(getContext());
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
switch (ev.getAction()){
case MotionEvent.ACTION_DOWN:
downX=ev.getX();
downY=ev.getY();
isIntercept=touchChildToTop();
break;
case MotionEvent.ACTION_MOVE:
if(!isIntercept)return false;
if(Math.abs(downY-ev.getY())>mTouchSlop){
//滑動開始
if(currentPage==1&&downY-ev.getY()<0){
isScrolling=true;
return true;
}else if(currentPage==0&&downY-ev.getY()>0){
isScrolling=true;
return true;
}
}
break;
case MotionEvent.ACTION_UP:
isScrolling=false;
break;
case MotionEvent.ACTION_CANCEL:
isScrolling=false;
break;
}
return super.onInterceptTouchEvent(ev);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
measureChildren(widthMeasureSpec,heightMeasureSpec);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if(pageOne==null){
pageOne=getChildAt(0);
}
if(pageTwo==null){
pageTwo=getChildAt(1);
}
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
if (pageOne!=null&&pageOne.getVisibility() != GONE) {
pageOne.layout(0+getPaddingLeft(), 0+getPaddingTop(), getWidth()-getPaddingRight(), getHeight()-getPaddingBottom());
}
if (pageTwo!=null&&pageTwo.getVisibility() != GONE) {
pageTwo.layout(0+getPaddingLeft(), getHeight(), getWidth()-getPaddingRight(), getHeight()+getHeight()-getPaddingBottom());
}
}
private float downX;
private float downY;
private boolean isScrolling;
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
downX=event.getX();
downY=event.getY();
isIntercept=touchChildToTop();
break;
case MotionEvent.ACTION_MOVE:
if(currentPage==1&&downY-event.getY()<0){
isScrolling=true;
}else if(currentPage==0&&downY-event.getY()>0){
isScrolling=true;
}else {
isScrolling=false;
}
if(currentPage==1&&!isIntercept){
isScrolling=false;
}
if(isScrolling){
if(currentPage==0&&downY-event.getY()>0){//整體向上偏移,翻到下一頁
scrollTo(0,(int)(downY-event.getY()));
}else if(currentPage==1&&downY-event.getY()<0){//翻到上一頁
scrollTo(0,getHeight()+(int)(downY-event.getY()));
}else {
return false;
}
}else {
return false;
}
break;
case MotionEvent.ACTION_UP:
if(isScrolling){
if(currentPage==0){
if((downY-event.getY()>mPageChangeLimit)){
scroller.startScroll(0,getScrollY(),0,getHeight()-getScrollY(),500);
invalidate();
// scrollTo(0,getHeight());
currentPage=1;
if(mOnPageChangeListener!=null){
mOnPageChangeListener.onPageChanged(currentPage,pageTwo,true);
}
}else {
scroller.startScroll(0,getScrollY(),0,-getScrollY(),300);
invalidate();
// scrollTo(0,0);
}
}else {
if(Math.abs(downY-event.getY())>mPageChangeLimit){
scroller.startScroll(0,getScrollY(),0,-getScrollY(),500);
invalidate();
if(mOnPageChangeListener!=null){
mOnPageChangeListener.onPageChanged(currentPage,pageOne,true);
}
currentPage=0;
}else {
scroller.startScroll(0,getScrollY(),0,pageOne.getHeight()-getScrollY(),300);
invalidate();
// scrollTo(0,pageOne.getHeight());
}
}
isScrolling=false;
}
isIntercept=false;
break;
case MotionEvent.ACTION_CANCEL:
isScrolling=false;
scrollTo(0,0);
isIntercept=false;
break;
}
return true;
}
private WebView childOne;
private ListView childTwo;
private int curTouchChild;
public void setScrollerChildren(WebView childOne, ListView childTwo){
this.childOne=childOne;
this.childTwo=childTwo;
curTouchChild=0;
}
public void changeCurScrollerChild(int i){
curTouchChild=i;
}
//實現第二頁中包含可滑動控件時是否滑到頂部
//從而決定本控件是否處理touch事件。
private boolean touchChildToTop(){
if(curTouchChild==0&&childOne!=null&&childOne.getScrollY()<=0){
return true;
}else if(curTouchChild==1&&childTwo!=null&&childTwo.getScrollY()<=0&&childTwo.getFirstVisiblePosition()==0){
return true;
}else {
return false;
}
}
@Override
public void computeScroll() {
if(scroller.computeScrollOffset()){
scrollTo(0,scroller.getCurrY());
postInvalidate();
}
}
public void setOnPageChangeListener(OnPageChangeListener onPageChangeListener){
mOnPageChangeListener=onPageChangeListener;
}
public interface OnPageChangeListener{
public void onPageChanged(int page, View currentView,boolean bySelf);
}
}
MainActivity.java:
public class MainActivity extends AppCompatActivity {
TwoPageLayout mLayout;
WebView mWebView;
RadioGroup mTabs;
ListView mListview;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
private List
主要代碼就這幾部分。TowPageLayout這個布局的用法有點類似DrawerLayout,他只能擁有2個子視圖,看字面意思也就是2頁的布局嘛。主要需要注意的是,當第二頁有可滑動控件的情況,需要對事件攔截。滑動的動畫是通過OverScroller這個類進行輔助計算。當然要注意,OverScroller只是負責計算滑動過程中的坐標變化,並不會改變控件的滑動狀態,需要通過重繪來實現位置的改變。
源碼地址:https://github.com/qinzhen308/TwoPageView
Android界面編程——Android布局組件(二)
2.3.1 布局介紹 布局用於定義Activity中UI元素的排列結構,Android提供了LinearLayout線性布局、RelativeLayout相對布局 、Fr
Android AlarmManager鬧鐘實現
什麼是AlarmManager? AlarmManager是Android中常用的一種系統級別的提示服務,在特定的時刻為我們廣播一個指定的Intent。簡單
自定義帶數字選擇的checkBox
最近一直在做即時通訊,當然少不了發圖片了, 既然要發圖片,我連忙打開qq,看看qq發圖片是個什麼效果,看起來確實不錯,我就照著qq仿寫了一個,其中選擇圖片時,圖片的右上角
Android沉浸式狀態欄實現
蘋果上的UI基本上都是這個效果,然而Android機上的頂部狀態欄總是和app的主題顏色不搭。還好如今的api19以上的版本,我們也能做出這樣的效果。第一步: // 需