(一).前言:
這兩天QQ進行了重大更新(6.X)尤其在UI風格上面由之前的藍色換成了白色居多了,側滑效果也發生了一些變化,那我們今天來模仿實現一個QQ6.X版本的側滑介面效果。今天我們還是採用神器ViewDragHelper來實現.
本次實例具體程式碼已經上傳到下面的專案中,歡迎各位去star和fork一下。
https://github.com/jiangqqlmj/DragHelper4QQ
FastDev4Android框架專案位址:https://github.com/jiangqqlmj/FastDev4Android
(3338383對我們的基本使用我們的基本使用方法,同時也知道了裡邊的若干個方法的用途,下面我們還是把基本的使用步驟溫習一下。要使用ViewGragHelper實作子View拖曳移動的步驟如下:
建立ViewGragHelper實例(傳入Callback)
重寫事件攔截處理方法onInterceptTouch和onTouchEvent
實作Callback,實作中的相關方法或水平垂直方向移動的距離方法
更加具體分析大家可以看前一篇博客,或者我們今天這邊會透過具體實例講解一下。
(三).QQ5.X側滑效果實現分析:
在正式版本QQ中的側滑效果如下:
觀察上面我們可以理解為兩個View,一個是底部的相當於左側功能View,另外一個是上層主功能內容View,我們在上面進行拖曳上層View或者左右滑動的時候,上層和下層的View相應進行滑動以及View大小變化,同時加入相關的動畫。當然我們點擊上層的View可以進行開啟或關閉側滑選單。
(四).側滑效果自訂元件實作
1.首先我們這邊整合自FrameLayout建立一個自訂View DragLayout。內部的定義的一些變數如下(主要包括一些配置類,手勢,ViewDragHelper實例,螢幕寬高,拖曳的子視圖View等)
//是否带有阴影效果 private boolean isShowShadow = true; //手势处理类 private GestureDetectorCompat gestureDetector; //视图拖拽移动帮助类 private ViewDragHelper dragHelper; //滑动监听器 private DragListener dragListener; //水平拖拽的距离 private int range; //宽度 private int width; //高度 private int height; //main视图距离在ViewGroup距离左边的距离 private int mainLeft; private Context context; private ImageView iv_shadow; //左侧布局 private RelativeLayout vg_left; //右侧(主界面布局) private CustomRelativeLayout vg_main;
然後在內部還定義了一個回調接口主要處理拖曳過程中的一些頁面打開,關閉以及滑動中的事件回調:
/** * 滑动相关回调接口 */ public interface DragListener { //界面打开 public void onOpen(); //界面关闭 public void onClose(); //界面滑动过程中 public void onDrag(float percent); }
2.開始創建ViewDragHelper實例,依然在自定義View DragLayout初始化的時候創建,使用ViewGragHelper的靜態方法:
public DragLayout(Context context,AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); gestureDetector = new GestureDetectorCompat(context, new YScrollDetector()); dragHelper =ViewDragHelper.create(this, dragHelperCallback); }
其中create()方法創建的時候傳入了一個dragHelperCallBack回呼類,將會在第四點講到。
3.接著需要重寫ViewGroup中事件方法,攔截觸摸事件給ViewGragHelper內部進行處理,這樣達到拖拽移動子View視圖的目的;
/** * 拦截触摸事件 * @param ev * @return */ @Override public boolean onInterceptTouchEvent(MotionEvent ev) { return dragHelper.shouldInterceptTouchEvent(ev) &&gestureDetector.onTouchEvent(ev); } /** * 将拦截的到事件给ViewDragHelper进行处理 * @param e * @return */ @Override public boolean onTouchEvent(MotionEvent e){ try { dragHelper.processTouchEvent(e); } catch (Exception ex) { ex.printStackTrace(); } return false; }
這邊我們在onInterceptTouch攔截讓事件從父控制往子View中轉移,然後在onTouchEvent方法中攔截讓ViewDragHelper進行消費處理。
4.開始自訂建立ViewDragHelper.Callback的實例dragHelperCallback分別實作一個抽象方法tryCaptureView以及重寫以下若干個方法來實現側滑功能,下面一個個來看一下。
/** * 拦截所有的子View * @param child Child the user is attempting to capture * @param pointerId ID of the pointer attempting the capture * @return */ @Override public boolean tryCaptureView(Viewchild, int pointerId) { return true; }
該進行攔截ViewGroup(本例中為:DragLayout)中所有的子View,直接返回true,表示所有的子View都可以進行拖曳移動。
/** * 水平方向移动 * @param child Child view beingdragged * @param left Attempted motion alongthe X axis * @param dx Proposed change inposition for left * @return */ @Override public int clampViewPositionHorizontal(View child, int left, int dx) { if (mainLeft + dx < 0) { return 0; } else if (mainLeft + dx >range) { return range; } else { return left; } }
實作此方法表示水平方向滑動,同時方法中會進行判斷邊界值,例如當上面的main view已經向左移動邊界之外了,直接返回0,表示向左最左邊只能x=0;然後向右移動會判斷向右最變得距離range,至於range的初始化後邊會講到。除了這兩種情況之外,就是直接返回left即可。
/** * 设置水平方向滑动的最远距离 *@param child Child view to check 屏幕宽度 * @return */ @Override public int getViewHorizontalDragRange(View child) { return width; }
該方法有必要實現,因為該方法在Callback內部默認返回0,也就是說,如果的view的click事件為true,那麼會出現整個子View沒法拖曳移動的情況了。那麼這邊直接返回left view寬度了,表示水平方向滑動的最遠距離了。
/** * 当拖拽的子View,手势释放的时候回调的方法, 然后根据左滑或者右滑的距离进行判断打开或者关闭 * @param releasedChild * @param xvel * @param yvel */ @Override public void onViewReleased(View releasedChild, float xvel, float yvel) { super.onViewReleased(releasedChild,xvel, yvel); if (xvel > 0) { open(); } else if (xvel < 0) { close(); } else if (releasedChild == vg_main&& mainLeft > range * 0.3) { open(); } else if (releasedChild == vg_left&& mainLeft > range * 0.7) { open(); } else { close(); } }
該方法在拖曳子View移動手指釋放的時候被調用,這是會判斷移動向左,向右的意圖,進行打開或關閉man view(上層視圖)。以下是實作的最後一個方法:onViewPositionChanged
/** * 子View被拖拽 移动的时候回调的方法 * @param changedView View whoseposition changed * @param left New X coordinate of theleft edge of the view * @param top New Y coordinate of thetop edge of the view * @param dx Change in X position fromthe last call * @param dy Change in Y position fromthe last call */ @Override public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) { if (changedView == vg_main) { mainLeft = left; } else { mainLeft = mainLeft + left; } if (mainLeft < 0) { mainLeft = 0; } else if (mainLeft > range) { mainLeft = range; } if (isShowShadow) { iv_shadow.layout(mainLeft, 0,mainLeft + width, height); } if (changedView == vg_left) { vg_left.layout(0, 0, width,height); vg_main.layout(mainLeft, 0,mainLeft + width, height); } dispatchDragEvent(mainLeft); } };
該方法是在我們進行拖曳移動子View的過程中進行回調,根據移動座標位置,然後進行重新定義left view和main view。同時呼叫dispathDragEvent()方法進行拖曳事件相關處理分發同時根據狀態來回調介面:
/** * 进行处理拖拽事件 * @param mainLeft */ private void dispatchDragEvent(int mainLeft) { if (dragListener == null) { return; } float percent = mainLeft / (float)range; //根据滑动的距离的比例 animateView(percent); //进行回调滑动的百分比 dragListener.onDrag(percent); Status lastStatus = status; if (lastStatus != getStatus()&& status == Status.Close) { dragListener.onClose(); } else if (lastStatus != getStatus()&& status == Status.Open) { dragListener.onOpen(); } }
該方法中有一行程式碼float percent=mainLeft/(float)range;算到一個百分比後面會用到
5.至於子View佈局的獲取初始化以及寬高和水平滑動距離的大小設置方法:
/** * 布局加载完成回调 * 做一些初始化的操作 */ @Override protected void onFinishInflate() { super.onFinishInflate(); if (isShowShadow) { iv_shadow = new ImageView(context); iv_shadow.setImageResource(R.mipmap.shadow); LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); addView(iv_shadow, 1, lp); } //左侧界面 vg_left = (RelativeLayout)getChildAt(0); //右侧(主)界面 vg_main = (CustomRelativeLayout)getChildAt(isShowShadow ? 2 : 1); vg_main.setDragLayout(this); vg_left.setClickable(true); vg_main.setClickable(true); }
以及控件大小發生變化回調的方法:
@Override protected void onSizeChanged(int w, int h,int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); width = vg_left.getMeasuredWidth(); height = vg_left.getMeasuredHeight(); //可以水平拖拽滑动的距离 一共为屏幕宽度的80% range = (int) (width *0.8f); }
在該方法中我們可以實時獲取寬和高以及拖拽水平距離。
6.上面的所有核心代码都为使用ViewDragHelper实现子控件View拖拽移动的方法,但是根据我们这边侧滑效果还需要实现动画以及滑动过程中View的缩放效果,所以我们这边引入了一个动画开源库:
然后根据前面算出来的百分比来缩放View视图:
/** * 根据滑动的距离的比例,进行平移动画 * @param percent */ private void animateView(float percent) { float f1 = 1 - percent * 0.5f; ViewHelper.setTranslationX(vg_left,-vg_left.getWidth() / 2.5f + vg_left.getWidth() / 2.5f * percent); if (isShowShadow) { //阴影效果视图大小进行缩放 ViewHelper.setScaleX(iv_shadow, f1* 1.2f * (1 - percent * 0.10f)); ViewHelper.setScaleY(iv_shadow, f1* 1.85f * (1 - percent * 0.10f)); } }
7.当然除了上面这些还缺少一个效果就是,当我们滑动过程中假如我们手指释放,按照常理来讲view就不会在进行移动了,那么这边我们需要一个加速度当我们释放之后,还能保持一定的速度,该怎么样实现呢?答案就是实现computeScroll()方法。
/** * 有加速度,当我们停止滑动的时候,该不会立即停止动画效果 */ @Override public void computeScroll() { if (dragHelper.continueSettling(true)){ ViewCompat.postInvalidateOnAnimation(this); } }
OK上面关于DragLayout的核心代码就差不多这么多了,下面是使用DragLayout类来实现侧滑效果啦!
(五).侧滑效果组件使用
1.首先使用的布局文件如下:
<com.chinaztt.widget.DragLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/dl" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@android:color/transparent" > <!--下层 左边的布局--> <includelayoutincludelayout="@layout/left_view_layout"/> <!--上层 右边的主布局--> <com.chinaztt.widget.CustomRelativeLayout android:layout_width="match_parent" android:layout_height="match_parent" android:background="#FFFFFF" > <LinearLayout android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <RelativeLayout android:id="@+id/rl_title" android:layout_width="match_parent" android:layout_height="49dp" android:gravity="bottom" android:background="@android:color/holo_orange_light" > <includelayoutincludelayout="@layout/common_top_bar_layout"/> </RelativeLayout> <!--中间内容后面放入Fragment--> <FrameLayout android:layout_width="fill_parent" android:layout_height="fill_parent" > <fragment android:id="@+id/main_info_fragment" class="com.chinaztt.fragment.OneFragment" android:layout_width="fill_parent" android:layout_height="fill_parent"/> </FrameLayout> </LinearLayout> </com.chinaztt.widget.CustomRelativeLayout> </com.chinaztt.widget.DragLayout>
该布局文件中父层View就是DragLayout,然后内部有两个RelativeLayout布局,分别充当下一层布局和上一层主布局。
2.下面我们来看一下下层菜单布局,这边我专门写了一个left_view_layout.xml文件,其中主要分为三块,第一块顶部为头像个人基本信息布局,中间为功能入口列表,底部是设置等功能,具体布局代码如下:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingTop="70dp" android:background="@drawable/sidebar_bg" > <LinearLayout android:id="@+id/ll1" android:paddingLeft="30dp" android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="vertical"> <!--头像,昵称信息--> <LinearLayout android:layout_width="match_parent" android:layout_height="70dp" android:orientation="horizontal" android:gravity="center_vertical" > <com.chinaztt.widget.RoundAngleImageView android:id="@+id/iv_bottom" android:layout_width="50dp" android:layout_height="50dp" android:scaleType="fitXY" android:src="@drawable/icon_logo" app:roundWidth="25dp" app:roundHeight="25dp"/> <LinearLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:gravity="center_vertical" android:layout_gravity="center_vertical" android:orientation="vertical"> <RelativeLayout android:layout_width="fill_parent" android:layout_height="wrap_content" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerVertical="true" android:layout_marginLeft="15dp" android:text="名字:jiangqqlmj" android:textColor="@android:color/black" android:textSize="15sp" /> <ImageButton android:layout_alignParentRight="true" android:layout_centerVertical="true" android:layout_marginRight="100dp" android:layout_width="22dp" android:layout_height="22dp" android:background="@drawable/qrcode_selector"/> </RelativeLayout> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:layout_marginLeft="15dp" android:text="QQ:781931404" android:textColor="@android:color/black" android:textSize="13sp" /> </LinearLayout> </LinearLayout> <LinearLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:gravity="center_vertical" android:orientation="horizontal"> <ImageView android:layout_width="17dp" android:layout_height="17dp" android:scaleType="fitXY" android:src="@drawable/sidebar_signature_nor"/> <TextView android:layout_marginLeft="5dp" android:textSize="13sp" android:textColor="#676767" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="用心做产品!"/> </LinearLayout> </LinearLayout> <!--底部功能条--> <includelayoutincludelayout="@layout/left_view_bottom_layout" android:id="@+id/bottom_view" /> <!--中间列表--> <ListView android:id="@+id/lv" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_above="@id/bottom_view" android:layout_below="@id/ll1" android:layout_marginBottom="30dp" android:layout_marginTop="70dp" android:cacheColorHint="#00000000" android:listSelector="@drawable/lv_click_selector" android:divider="@null" android:scrollbars="none" android:textColor="#ffffff"/> </RelativeLayout>
该布局还是比较简单的,对于上层主内容布局这边就放一个顶部导航栏和中的Fragment内容信息,留着后期大家功能扩展即可。
3.主Activity使用如下:
public class MainActivity extends BaseActivity { private DragLayout dl; private ListView lv; private ImageView iv_icon, iv_bottom; private QuickAdapter<ItemBean> quickAdapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); setStatusBar(); initDragLayout(); initView(); } private void initDragLayout() { dl = (DragLayout) findViewById(R.id.dl); dl.setDragListener(new DragLayout.DragListener() { //界面打开的时候 @Override public void onOpen() { } //界面关闭的时候 @Override public void onClose() { } //界面滑动的时候 @Override public void onDrag(float percent) { ViewHelper.setAlpha(iv_icon, 1 - percent); } }); } private void initView() { iv_icon = (ImageView) findViewById(R.id.iv_icon); iv_bottom = (ImageView) findViewById(R.id.iv_bottom); lv = (ListView) findViewById(R.id.lv); lv.setAdapter(quickAdapter=new QuickAdapter<ItemBean>(this,R.layout.item_left_layout, ItemDataUtils.getItemBeans()) { @Override protected void convert(BaseAdapterHelper helper, ItemBean item) { helper.setImageResource(R.id.item_img,item.getImg()) .setText(R.id.item_tv,item.getTitle()); } }); lv.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> arg0, View arg1, int position, long arg3) { Toast.makeText(MainActivity.this,"Click Item "+position,Toast.LENGTH_SHORT).show(); } }); iv_icon.setOnClickListener(new OnClickListener() { @Override public void onClick(View arg0) { dl.open(); } }); } }
初始化控件,设置滑动监听器,以及左侧菜单功能列表设置即可了,不过上面大家应该看了QuickAdapter的使用,该为BaseAdapterHelper框架使用,我们需要在项目build.gradle中作如下配置:
具体关于BaseAdapter的使用讲解博客地址如下:
4.正式运行效果如下:
5.因为这边底层需要ViewDragHelper类,所以大家在使用的时候需要导入V4包的,不过我这边直接把ViewGragHelper类的源代码复制到项目中了。
(六).DragLayout源代码带注释
上面主要分析DragLayout的具体实现,不过我这边也贴一下DragLayout带有注释的全部源代码让大家可以更好的了解DragLayout的具体实现代码:
/** *使用ViewRragHelper实现侧滑效果功能 */ publicclass DragLayout extends FrameLayout { private boolean isShowShadow = true; //手势处理类 private GestureDetectorCompat gestureDetector; //视图拖拽移动帮助类 private ViewDragHelper dragHelper; //滑动监听器 private DragListener dragListener; //水平拖拽的距离 private int range; //宽度 private int width; //高度 private int height; //main视图距离在ViewGroup距离左边的距离 private int mainLeft; private Context context; private ImageView iv_shadow; //左侧布局 private RelativeLayout vg_left; //右侧(主界面布局) private CustomRelativeLayout vg_main; //页面状态 默认为关闭 private Status status = Status.Close; public DragLayout(Context context) { this(context, null); } public DragLayout(Context context,AttributeSet attrs) { this(context, attrs, 0); this.context = context; } public DragLayout(Context context,AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); gestureDetector = new GestureDetectorCompat(context, new YScrollDetector()); dragHelper =ViewDragHelper.create(this, dragHelperCallback); } class YScrollDetector extends SimpleOnGestureListener { @Override public boolean onScroll(MotionEvent e1,MotionEvent e2, float dx, float dy) { return Math.abs(dy) <=Math.abs(dx); } } /** * 实现子View的拖拽滑动,实现Callback当中相关的方法 */ private ViewDragHelper.Callback dragHelperCallback = new ViewDragHelper.Callback() { /** * 水平方向移动 * @param child Child view beingdragged * @param left Attempted motion alongthe X axis * @param dx Proposed change inposition for left * @return */ @Override public int clampViewPositionHorizontal(View child, int left, int dx) { if (mainLeft + dx < 0) { return 0; } else if (mainLeft + dx >range) { return range; } else { return left; } } /** * 拦截所有的子View * @param child Child the user isattempting to capture * @param pointerId ID of the pointerattempting the capture * @return */ @Override public boolean tryCaptureView(View child, int pointerId) { return true; } /** * 设置水平方向滑动的最远距离 *@param child Child view to check 屏幕宽度 * @return */ @Override public int getViewHorizontalDragRange(View child) { return width; } /** * 当拖拽的子View,手势释放的时候回调的方法, 然后根据左滑或者右滑的距离进行判断打开或者关闭 * @param releasedChild * @param xvel * @param yvel */ @Override public void onViewReleased(View releasedChild, float xvel, float yvel) { super.onViewReleased(releasedChild,xvel, yvel); if (xvel > 0) { open(); } else if (xvel < 0) { close(); } else if (releasedChild == vg_main&& mainLeft > range * 0.3) { open(); } else if (releasedChild == vg_left&& mainLeft > range * 0.7) { open(); } else { close(); } } /** * 子View被拖拽 移动的时候回调的方法 * @param changedView View whoseposition changed * @param left New X coordinate of theleft edge of the view * @param top New Y coordinate of thetop edge of the view * @param dx Change in X position fromthe last call * @param dy Change in Y position fromthe last call */ @Override public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) { if (changedView == vg_main) { mainLeft = left; } else { mainLeft = mainLeft + left; } if (mainLeft < 0) { mainLeft = 0; } else if (mainLeft > range) { mainLeft = range; } if (isShowShadow) { iv_shadow.layout(mainLeft, 0,mainLeft + width, height); } if (changedView == vg_left) { vg_left.layout(0, 0, width,height); vg_main.layout(mainLeft, 0,mainLeft + width, height); } dispatchDragEvent(mainLeft); } }; /** * 滑动相关回调接口 */ public interface DragListener { //界面打开 public void onOpen(); //界面关闭 public void onClose(); //界面滑动过程中 public void onDrag(float percent); } public void setDragListener(DragListener dragListener) { this.dragListener = dragListener; } /** * 布局加载完成回调 * 做一些初始化的操作 */ @Override protected void onFinishInflate() { super.onFinishInflate(); if (isShowShadow) { iv_shadow = new ImageView(context); iv_shadow.setImageResource(R.mipmap.shadow); LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); addView(iv_shadow, 1, lp); } //左侧界面 vg_left = (RelativeLayout)getChildAt(0); //右侧(主)界面 vg_main = (CustomRelativeLayout)getChildAt(isShowShadow ? 2 : 1); vg_main.setDragLayout(this); vg_left.setClickable(true); vg_main.setClickable(true); } public ViewGroup getVg_main() { return vg_main; } public ViewGroup getVg_left() { return vg_left; } @Override protected void onSizeChanged(int w, int h,int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); width = vg_left.getMeasuredWidth(); height = vg_left.getMeasuredHeight(); //可以水平拖拽滑动的距离 一共为屏幕宽度的80% range = (int) (width * 0.8f); } /** * 调用进行left和main 视图进行位置布局 * @param changed * @param left * @param top * @param right * @param bottom */ @Override protected void onLayout(boolean changed,int left, int top, int right, int bottom) { vg_left.layout(0, 0, width, height); vg_main.layout(mainLeft, 0, mainLeft +width, height); } /** * 拦截触摸事件 * @param ev * @return */ @Override public boolean onInterceptTouchEvent(MotionEvent ev) { returndragHelper.shouldInterceptTouchEvent(ev) &&gestureDetector.onTouchEvent(ev); } /** * 将拦截的到事件给ViewDragHelper进行处理 * @param e * @return */ @Override public boolean onTouchEvent(MotionEvent e){ try { dragHelper.processTouchEvent(e); } catch (Exception ex) { ex.printStackTrace(); } return false; } /** * 进行处理拖拽事件 * @param mainLeft */ private void dispatchDragEvent(intmainLeft) { if (dragListener == null) { return; } float percent = mainLeft / (float)range; //滑动动画效果 animateView(percent); //进行回调滑动的百分比 dragListener.onDrag(percent); Status lastStatus = status; if (lastStatus != getStatus()&& status == Status.Close) { dragListener.onClose(); } else if (lastStatus != getStatus()&& status == Status.Open) { dragListener.onOpen(); } } /** * 根据滑动的距离的比例,进行平移动画 * @param percent */ private void animateView(float percent) { float f1 = 1 - percent * 0.5f; ViewHelper.setTranslationX(vg_left,-vg_left.getWidth() / 2.5f + vg_left.getWidth() / 2.5f * percent); if (isShowShadow) { //阴影效果视图大小进行缩放 ViewHelper.setScaleX(iv_shadow, f1* 1.2f * (1 - percent * 0.10f)); ViewHelper.setScaleY(iv_shadow, f1* 1.85f * (1 - percent * 0.10f)); } } /** * 有加速度,当我们停止滑动的时候,该不会立即停止动画效果 */ @Override public void computeScroll() { if (dragHelper.continueSettling(true)){ ViewCompat.postInvalidateOnAnimation(this); } } /** * 页面状态(滑动,打开,关闭) */ public enum Status { Drag, Open, Close } /** * 页面状态设置 * @return */ public Status getStatus() { if (mainLeft == 0) { status = Status.Close; } else if (mainLeft == range) { status = Status.Open; } else { status = Status.Drag; } return status; } public void open() { open(true); } public void open(boolean animate) { if (animate) { //继续滑动 if(dragHelper.smoothSlideViewTo(vg_main, range, 0)) { ViewCompat.postInvalidateOnAnimation(this); } } else { vg_main.layout(range, 0, range * 2,height); dispatchDragEvent(range); } } public void close() { close(true); } public void close(boolean animate) { if (animate) { //继续滑动 if(dragHelper.smoothSlideViewTo(vg_main, 0, 0)) { ViewCompat.postInvalidateOnAnimation(this); } } else { vg_main.layout(0, 0, width,height); dispatchDragEvent(0); } } }
(七).最后总结
今天我们实现打造QQ最新版本QQ6.X效果,同时里边用到了ViewDragHelper,BaseAdapterHelper的运用,具体该知识点的使用方法,我已经在我的博客中更新讲解的文章,欢迎大家查看。
更多Android使用ViewDragHelper實作QQ6.X最新版本側滑介面效果實例程式碼相关文章请关注PHP中文网!

新興技術對Java的平台獨立性既有威脅也有增強。 1)雲計算和容器化技術如Docker增強了Java的平台獨立性,但需要優化以適應不同雲環境。 2)WebAssembly通過GraalVM編譯Java代碼,擴展了其平台獨立性,但需與其他語言競爭性能。

不同JVM實現都能提供平台獨立性,但表現略有不同。 1.OracleHotSpot和OpenJDKJVM在平台獨立性上表現相似,但OpenJDK可能需額外配置。 2.IBMJ9JVM在特定操作系統上表現優化。 3.GraalVM支持多語言,需額外配置。 4.AzulZingJVM需特定平台調整。

平台獨立性通過在多種操作系統上運行同一套代碼,降低開發成本和縮短開發時間。具體表現為:1.減少開發時間,只需維護一套代碼;2.降低維護成本,統一測試流程;3.快速迭代和團隊協作,簡化部署過程。

Java'splatformindependencefacilitatescodereusebyallowingbytecodetorunonanyplatformwithaJVM.1)Developerscanwritecodeonceforconsistentbehavioracrossplatforms.2)Maintenanceisreducedascodedoesn'tneedrewriting.3)Librariesandframeworkscanbesharedacrossproj

要解決Java應用程序中的平台特定問題,可以採取以下步驟:1.使用Java的System類查看系統屬性以了解運行環境。 2.利用File類或java.nio.file包處理文件路徑。 3.根據操作系統條件加載本地庫。 4.使用VisualVM或JProfiler優化跨平台性能。 5.通過Docker容器化確保測試環境與生產環境一致。 6.利用GitHubActions在多個平台上進行自動化測試。這些方法有助於有效地解決Java應用程序中的平台特定問題。

類加載器通過統一的類文件格式、動態加載、雙親委派模型和平台無關的字節碼,確保Java程序在不同平台上的一致性和兼容性,實現平台獨立性。

Java編譯器生成的代碼是平台無關的,但最終執行的代碼是平台特定的。 1.Java源代碼編譯成平台無關的字節碼。 2.JVM將字節碼轉換為特定平台的機器碼,確保跨平台運行但性能可能不同。

多線程在現代編程中重要,因為它能提高程序的響應性和資源利用率,並處理複雜的並發任務。 JVM通過線程映射、調度機制和同步鎖機制,在不同操作系統上確保多線程的一致性和高效性。


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

WebStorm Mac版
好用的JavaScript開發工具

SublimeText3 Linux新版
SublimeText3 Linux最新版

VSCode Windows 64位元 下載
微軟推出的免費、功能強大的一款IDE編輯器

MinGW - Minimalist GNU for Windows
這個專案正在遷移到osdn.net/projects/mingw的過程中,你可以繼續在那裡關注我們。 MinGW:GNU編譯器集合(GCC)的本機Windows移植版本,可自由分發的導入函式庫和用於建置本機Windows應用程式的頭檔;包括對MSVC執行時間的擴展,以支援C99功能。 MinGW的所有軟體都可以在64位元Windows平台上運作。

記事本++7.3.1
好用且免費的程式碼編輯器