編輯:關於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
2.3.1 布局介紹 布局用於定義Activity中UI元素的排列結構,Android提供了LinearLayout線性布局、RelativeLayout相對布局 、Fr
什麼是AlarmManager? AlarmManager是Android中常用的一種系統級別的提示服務,在特定的時刻為我們廣播一個指定的Intent。簡單
最近一直在做即時通訊,當然少不了發圖片了, 既然要發圖片,我連忙打開qq,看看qq發圖片是個什麼效果,看起來確實不錯,我就照著qq仿寫了一個,其中選擇圖片時,圖片的右上角
蘋果上的UI基本上都是這個效果,然而Android機上的頂部狀態欄總是和app的主題顏色不搭。還好如今的api19以上的版本,我們也能做出這樣的效果。第一步: // 需