編輯:關於Android編程
好了,跟隨潮流,還是先看下效果,不然可能都沒人想看下去了(不會看到效果後不想看了吧O(∩_∩)O~)
vcHL19S2qNLlv9i8/rXEyrG68iy7ubvhvfjQ0NbY0MKy4sG/us2yvL7WLjwvcD4NCjxwPjxpbWcgYWx0PQ=="重寫前" src="/uploadfile/Collfiles/20160624/20160624092943692.png" title="\" />
嗯,就是讓左面板在主面板的下面,所以我們自定義的控件SlideLayout繼承FrameLayout.一般自定義控件會涉及到三個方法,onMeasure 測量,onLayout布局,onDraw繪制,如果是繼承ViewGroup的話我們一般需要重寫布局方法,繼承view的話要重寫onDraw方法,當然你喜歡的話,都可以重寫.我們這裡繼承的是FrameLayout,它繼承的是ViewGroup,即它已經實現了布局方法,但是我說過了,我們要重新測量和布局,主要是對左面板slideView的測量和布局,我們要調整它的寬度和位置,所以我們先來重寫onMeasure方法.onMeasure(int widthMeasureSpec, int heightMeasureSpec)看這兩個參數,看名字就知道中式英語很像有沒有,測量說明書,即我們能通過測量說明書來進行測量,其實這兩個數是測量說明書上給定的規范值,所以叫測量規范,結合我們下面的代碼進行講解
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
mSlideView = getChildAt(0);
mMainView = getChildAt(1);
mWd_width = MeasureSpec.getSize(widthMeasureSpec); //使用測量說明書得到我們想要知道的寬度和高度值,它是match_parent的,所以拿到的就是窗體的寬高度
mWd_height = MeasureSpec.getSize(heightMeasureSpec);
int sl_widthSpec = MeasureSpec.makeMeasureSpec((mWd_width /3) * 2,MeasureSpec.EXACTLY); //制作測量說明書規定值
int sl_heightSpec = MeasureSpec.makeMeasureSpec(mWd_height,MeasureSpec.EXACTLY);
mSlideView.measure(sl_widthSpec,sl_heightSpec);
mMainView.measure(widthMeasureSpec,heightMeasureSpec); //主面板和自己一樣,都是填充整個窗體,所以測量說明書的規定值一樣
setMeasuredDimension(mWd_width, mWd_height); //測量自己用這個方法.
}
制作測量規范值的函數,第一個參數的意思是,你想要分配多少像素吧,沒有給解釋,這個不重要….重要的是第二個參數,第二個參數有三個值可以填,分別是UNSPECIFIED,EXACTLY,AT_MOST,我們看它們單詞的意思就好理解了,第一個說不確定,就叫它自己看著辦;第二個意思是精確的,即我們生活中確定以及肯定,它會禁它最大的努力去按第一個參數的值測量,你的值填的離譜,它也滿足不了.第三個就和第二個挺像了,盡量,就是比精確的肯定性差一點.所以我們如果自己確定一個測量規范的話,我確定我要側滑面板占屏幕的三分之二,所以我們這裡用EXACTLY.測量好了,接著就是我們的布局了
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
mSl_width = mSlideView.getWidth();
mSlideView.layout(mSlideView.getLeft() - mSl_width /3 ,mSlideView.getTop(),mSlideView.getRight(),mSlideView.getBottom());
}
布局的時候,說明測量已經完成了,所以我們是可以拿到我們左面板的寬度的,然後執行layout(l,t,r,b)看參數就明白了,這個不講了,我把它的left設為-自己看的三分之一,是自己為了做的像一點的想法.來看看我們重寫測量布局後的效果
好了,這時候開始我們的拖動了.視圖拖動我們用這個類ViewDragHelper,視圖拖動幫助者,首選看看怎麼得到它的對象static ViewDragHelper create(ViewGroup forParent, float sensitivity, Callback cb)它是個靜態方法,所以我們不是new出來的,第一個參數說拖動VIEW的父容器是誰,因為我們拖動的是主面板和左面板,所以他們的父容器是自己,即填this,第二個參數是靈敏度,它還有一個重載函數,只有兩個參數,這個參數沒寫默認是1.0f,所以我們一般也填1.0f.第三個參數是ViewDragHelper 的內部類Callback ,我們需要寫個類繼承它.
class MyDragCallBack extends ViewDragHelper.Callback {
@Override //表示要捕獲那個孩子,即處理哪個孩子的拖到事件,返回false不處理
public boolean tryCaptureView(View child, int pointerId) {
return true;
}
這個是抽象方法,我們必須實現,因為兩個孩子都要拖動,所以直接返回true,要像橫向拖動,我們還得重寫它的一個方法
@Override //
public int clampViewPositionHorizontal(View child, int left, int dx) { //left相對於屏幕左側的偏移值,是拖動的建議值
if (child == mMainView){ //主面板拖動范圍
if (left < 0){
left = 0;
}else if (left > (mSl_width)){
left = mSl_width;
}
}else if (child == mSlideView){ //左面板拖動范圍
if (left > 0){
left = 0;
}else if (left < -mSl_width){
left = mSl_width;
}
}
return left;
}
它默認是返回0的,即拖動不了的,同理它還有縱向的,這裡我們不需要,所以不重新,到這裡還是拖不動的,因為,我們的ViewDragHelper拚什麼拖動呢,我們還要把touch事件傳遞給他,它才能決定該不該拖動
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
return mViewDragHelper.shouldInterceptTouchEvent(event); //交給它去判斷該不該攔截
}
@Override
public boolean onTouchEvent(MotionEvent event) {
try {
mViewDragHelper.processTouchEvent(event); //有可能會出錯,所以try一下
}catch (Exception e){
e.printStackTrace();
}
return true;
}
好了,這裡就可以拖動了,但是,只是一個view拖動了,另一個並不跟著動啊,所以我們還要另外重寫一個函數,onViewPositionChanged看名字就知道了吧,拖動的view改變的時候調用,所以我們在這裡動態的layout就好了
@Override
public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
super.onViewPositionChanged(changedView, left, top, dx, dy);
if (changedView == mMainView){
mSlideView.layout( left/3 - mSl_width/3,mSlideView.getTop(),left/3 - mSl_width/3+mSl_width,mSlideView.getBottom());
}else if (changedView == mSlideView){
mMainView.layout(mSl_width + left,mMainView.getTop(),mSl_width + left + mWd_width,mMainView.getBottom());
}
invalidate();
}
還有,當松開手的時候,我們應該看看左面板滑出來多少了,然後根據值判斷是不是該關閉,即我們重寫onViewReleased這個方法
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
if (mMainView.getLeft() < mSl_width/2){
closeMenu();
}else {
openMenu();
}
}
@Override
public void computeScroll() { //不停計算,不停調用
if (mViewDragHelper.continueSettling(true)){
ViewCompat.postInvalidateOnAnimation(this); //不停刷新直到停止滑動
}
}
public void closeMenu() {
mViewDragHelper.smoothSlideViewTo(mMainView,0,mMainView.getTop());
ViewCompat.postInvalidateOnAnimation(this); //兼容,刷新界面.
mMenuIsOpen = false;
}
public void openMenu() {
mViewDragHelper.smoothSlideViewTo(mMainView,mSl_width,mMainView.getTop());
ViewCompat.postInvalidateOnAnimation(this);
mSlideView.layout(0,mSlideView.getTop(),mSl_width,mSlideView.getBottom());
mMenuIsOpen = true;
}
在這裡介紹下smoothSlideViewTo(View child, int finalLeft, int finalTop)看參數就明白了是吧,但是實現它的效果我們要ViewCompat.postInvalidateOnAnimation(this)刷新界面,這是兼容國產各大rom的,還要重寫computeScroll()這個方法,continueSettling(true)返回true表示還沒滑玩,所以繼續刷新界面.好了到這裡我們就做的差不多了,來看看效果吧
好吧,為什麼會生成倒的gif圖片我也不知道,因為總是大了,弄了好幾次,把握不到度啊,治療治療頸椎病吧.看到這應該發現問題了吧,當在ViewGroup和listView上左右滑時劃不動.看到這也許有人說了,去ononInterceptTouchEvent那設置滑動判斷然後返回true攔截事件,再去Listview 的onTouchEvent判斷返回false表示我不消費這個事件,我想說我都做了,而且log顯示進入了我的判斷攔截事件.還是不行,我都開始懷疑人生.懷疑我對touch事件的認知,就去網上看別人怎麼說,說的都是我認知的,都不能解決這個問題.真的而且36度的天啊,很熱,很沮喪的感覺.反正弄了將近一天,真的////各種自己重寫view攔截…全部沒用…所以沒原理沒方法真的基本做不出來..突然重寫了ViewDragHelper.callback的一個方法就好了,是的,就是這個方法,我真的無法解釋,我都無語了.因為我想我事件都交給ViewDragHelper處理了,點ViewDragHelper.shouldInterceptTouchEvent(event)進去看源碼,看不懂…看到一個變量名有drag什麼,所以試一試的心態重寫了這個方法的.真的不知道這是撒原理,平常我們不重寫這個不是也能拖動麼,所以很少重寫,以後我每次都重寫了
@Override
public int getViewHorizontalDragRange(View child) {
// 返回拖拽的范圍
return mSl_width/3 * 2;
}
重寫後效果就能拖了,各控件的touch事件也正常,到這基本完了,最後來弄我們的拖動監聽吧
public interface OnSlideListener{
/**
* @param view //觸摸的是主界面還是菜單界面
* @param left //滑動了多長,負數向左滑,正數向右
* @param persent //滑動相對於總長度的百分比
*/
void onSlide(View view,int left,float persent);
}
private OnSlideListener mOnSlideListener;
public void setOnSlideListener(OnSlideListener listener){
mOnSlideListener = listener;
}
在 onViewPositionChanged中添加
if (mOnSlideListener != null){
mOnSlideListener.onSlide(changedView,left,left * 1.0f / mSl_width);
}
嗯,這裡還是貼下完整代碼吧,等下資源審核通過後我把項目文件下載路徑放在最下面,感興趣的朋友可以去下載看看
import android.content.Context;
import android.support.v4.view.ViewCompat;
import android.support.v4.widget.ViewDragHelper;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.FrameLayout;
/**
* Created by Root on 2016/6/20.
*/
public class SlideLayout extends FrameLayout{
private View mSlideView;
private View mMainView;
private ViewDragHelper mViewDragHelper;
private int mWd_width;
private int mWd_height;
private int mSl_width;
private boolean mMenuIsOpen;
public SlideLayout(Context context) {
this(context,null);
}
public SlideLayout(Context context, AttributeSet attrs) {
this(context, attrs,0);
}
public SlideLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mViewDragHelper = ViewDragHelper.create(this,1.0f,new MyDragCallBack());
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
mSlideView = getChildAt(0);
mMainView = getChildAt(1);
mWd_width = MeasureSpec.getSize(widthMeasureSpec); //使用測量說明書得到我們想要知道的寬度和高度值
mWd_height = MeasureSpec.getSize(heightMeasureSpec);
int sl_widthSpec = MeasureSpec.makeMeasureSpec((mWd_width /3) * 2,MeasureSpec.EXACTLY); //制作測量說明書規定值
int sl_heightSpec = MeasureSpec.makeMeasureSpec(mWd_height,MeasureSpec.EXACTLY);
mSlideView.measure(sl_widthSpec,sl_heightSpec);
mMainView.measure(widthMeasureSpec,heightMeasureSpec); //主面板和自己一樣,都是填充整個窗體,所以測量說明書的規定值一樣
setMeasuredDimension(mWd_width, mWd_height); //測量自己用這個方法.
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
mSl_width = mSlideView.getWidth();
mSlideView.layout(mSlideView.getLeft() - mSl_width /3 ,mSlideView.getTop(),mSlideView.getRight(),mSlideView.getBottom());
}
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
return mViewDragHelper.shouldInterceptTouchEvent(event); //交給它去判斷該不該攔截
}
@Override
public boolean onTouchEvent(MotionEvent event) {
try {
mViewDragHelper.processTouchEvent(event); //有可能會出錯,所以try一下
}catch (Exception e){
e.printStackTrace();
}
return true;
}
class MyDragCallBack extends ViewDragHelper.Callback {
@Override //表示要捕獲那個孩子,即處理哪個孩子的拖到事件,返回false不處理
public boolean tryCaptureView(View child, int pointerId) {
return true;
}
@Override //
public int clampViewPositionHorizontal(View child, int left, int dx) { //left相對於屏幕左側的偏移值,是拖動的建議值
if (child == mMainView){ //主面板拖動范圍
if (left < 0){
left = 0;
}else if (left > (mSl_width)){
left = mSl_width;
}
}else if (child == mSlideView){ //左面板拖動范圍
if (left > 0){
left = 0;
}else if (left < -mSl_width){
left = mSl_width;
}
}
return left;
}
@Override
public int getViewHorizontalDragRange(View child) {
// 返回拖拽的范圍
return mSl_width/3 * 2;
}
@Override
public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
super.onViewPositionChanged(changedView, left, top, dx, dy);
if (changedView == mMainView){
mSlideView.layout( left/3 - mSl_width/3,mSlideView.getTop(),left/3 - mSl_width/3+mSl_width,mSlideView.getBottom());
}else if (changedView == mSlideView){
mMainView.layout(mSl_width + left,mMainView.getTop(),mSl_width + left + mWd_width,mMainView.getBottom());
}
if (mOnSlideListener != null){
mOnSlideListener.onSlide(changedView,left,left * 1.0f / mSl_width);
}
invalidate();
}
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
if (mMainView.getLeft() < mSl_width/2){
closeMenu();
}else {
openMenu();
}
}
}
@Override
public void computeScroll() { //不停計算,不停調用
if (mViewDragHelper.continueSettling(true)){
ViewCompat.postInvalidateOnAnimation(this); //不停刷新直到停止滑動
}
}
public void closeMenu() {
mViewDragHelper.smoothSlideViewTo(mMainView,0,mMainView.getTop());
ViewCompat.postInvalidateOnAnimation(this); //兼容,刷新界面.
mMenuIsOpen = false;
}
public void openMenu() {
mViewDragHelper.smoothSlideViewTo(mMainView,mSl_width,mMainView.getTop());
ViewCompat.postInvalidateOnAnimation(this);
mSlideView.layout(0,mSlideView.getTop(),mSl_width,mSlideView.getBottom());
mMenuIsOpen = true;
}
public boolean isMenuIsOpen(){
return mMenuIsOpen;
}
public interface OnSlideListener{
/**
* @param view //觸摸的是主界面還是菜單界面
* @param left //滑動了多長,負數向左滑,正數向右
* @param persent //滑動相對於總長度的百分比
*/
void onSlide(View view,int left,float persent);
}
private OnSlideListener mOnSlideListener;
public void setOnSlideListener(OnSlideListener listener){
mOnSlideListener = listener;
}
}
在我們第一次安裝app的時候,有一些app會出現一個覆蓋在我們原來View上面的浮層view,用來做一個app的指示,讓用戶更快的知道app的整體架構和功能點。下面大家看
下面給大家展示了AndroidHttpClient結構:public final classAndroidHttpClientextends Objectimplemen
繼上篇json解析,我用了原生的json解析,但是在有些情況下我們不得不承認,一些優秀的json解析框架確實十分的好用,今天我們為了博客的保質保量,也就不分開寫,我們直接
1.普通側滑效果圖: 思路:通過自定義View繼承HorizontalScrollView,然後重寫onMeasure(),onLayout(),onTouch