編輯:關於Android編程
開源項目:Android-ObservableScrollView**
項目依賴添加:compile ‘com.github.ksoichiro:android-observablescrollview:1.6.0’(版本號自己參考下面地址提供)
開源地址:https://github.com/ksoichiro/Android-ObservableScrollView
效果圖(下圖來自上面鏈接):
以上效果雖然官方已有了具體實現,個人而言還是比較喜歡用開源的,最近有了個習慣,開源項目如果要用,那麼最少要把源碼看一遍,雖說不能完全看懂,但是至少要達到基於源碼庫定制開發沒問題。廢話不多說,下面看看ObservableScrollView結構
粗略一看貌似各個自定義控件都實現了Scrollable接口,就先從它相關類開始吧。
枚舉類型ScrollState定義滑動了方向
/**
* 滑動組件的滑動狀態
*/
public enum ScrollState {
/**
* 組件停止滑動
* 這個狀態並不代表滑動控件從為滑動
*/
STOP,
/**
*上滑動
*/
UP,
/**
* 向下滑動
*/
DOWN,
}
滑動輔助工具類ScrollUtils(以下的代碼塊方法具體實現自行參考源碼這裡避免篇幅太長,全部去掉),表示工具類的CMYK與ARGB轉換有點懵,為什麼要小於1呢,如有知道還請不吝賜教
/**
* 滑動效果輔助類
*/
public final class ScrollUtils {
/**
* 私有構造防止new實例
*/
private ScrollUtils() {
}
/**
* 返回一個有效值,該值的范圍在【minValue,MaxValue】
*/
public static float getFloat(final float value, final float minValue, final float maxValue) {
}
/**
* 創建一個帶透明度的color的顏色值,透明度范圍0-255
* 背景色改變用到它
* @param alpha 透明度
* @param baseColor 傳入顏色值
*/
public static int getColorWithAlpha(float alpha, int baseColor) {
}
/**
* 為視圖添加監聽,不需要的時候需要移除監聽器
* @param view 監聽的目標視圖
* @param runnable Runnable to be executed after the view is laid out.
*/
public static void addOnGlobalLayoutListener(final View view, final Runnable runnable) {
}
/**
* 混合兩種顏色。並指定透明度
* @param fromColor
* @param toColor .
* @param toAlpha
* @return Mixed color value in ARGB. Alpha is fixed value (255).
*/
public static int mixColors(int fromColor, int toColor, float toAlpha) {
}
/**
* RGB顏色轉換為CMYK顏色。
*
* @param rgbColor Target color.
* @return CMYK array.
*/
public static float[] cmykFromRgb(int rgbColor) {
}
/**
* CYMK顏色轉換為RGB顏色。
* 這個方法不會檢查是否非空cmyk或有4個元素的數組。
*
* @param cmyk 目標CYMK顏色。每個值應該在0.0到1.0 f之間, 應設置在這個秩序:青色,品紅色,黃色,黑色。
* @return ARGB color. Alpha is fixed value (255).
*/
public static int rgbFromCmyk(float[] cmyk) {
}
}
Touch事件是否攔截相關容器和監聽器的定義如下
/**
* 一個布局接觸滑動事件的攔截,布局提供移動滾動視圖的容器使用滾動的位置。
* 請注意,這個類覆蓋或使用觸摸事件API,如onTouchEvent onInterceptTouchEvent dispatchTouchEvent
* 所以要小心當你處理觸摸這個事件。
*/
public class TouchInterceptionFrameLayout extends FrameLayout {
/**
* 觸摸事件回調接口
*/
public interface TouchInterceptionListener {
/**
* 判斷是否要攔截當前視圖的觸摸事件
*
* @param ev Motion event.
* @param moving 是否在Action_Move移動中
* @param diffX 如果實在移動(ACTION_MOVE)返回滑動的距離X
* @param diffY 如果實在移動(ACTION_MOVE)返回滑動的距離Y
* @return 返回是否攔截布局
*/
boolean shouldInterceptTouchEvent(MotionEvent ev, boolean moving, float diffX, float diffY);
/**
* 攔截ACTION_DOWN事件
*
* @param ev Motion event.
*/
void onDownMotionEvent(MotionEvent ev);
/**
* 攔截ACTION_MOVE事件並且攜帶參數滑動距離
*
* @param ev Motion event.
* @param diffX Difference between previous X and current X.
* @param diffY Difference between previous Y and current Y.
*/
void onMoveMotionEvent(MotionEvent ev, float diffX, float diffY);
/**
* 攔截ACTION_UP或者ACTION_CANCEL事件
*
* @param ev Motion event.
*/
void onUpOrCancelMotionEvent(MotionEvent ev);
}
/**
* 是否攔截
*/
private boolean mIntercepting;
/**
* 是否攔截ACTION_DOWN
*/
private boolean mDownMotionEventPended;
/**
* 剛進入ACTION_DOWN是否直接攔截
*/
private boolean mBeganFromDownMotionEvent;
/**
* 子view取消touch 事件
*/
private boolean mChildrenEventsCanceled;
/**
* 記錄初始數據得Point
*/
private PointF mInitialPoint;
//...............................
public void setScrollInterceptionListener(TouchInterceptionListener listener) {
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (mTouchInterceptionListener == null) {
//如果攔截Listener都不存在直接不攔截
return false;
}
// 在這裡,我們必須接觸狀態變量進行初始化
// 問我們是否應該攔截這個事件。
// 我們是否應該攔截保存後的事件處理。
// private static native int nativeGetAction(long nativePtr); native方法獲取Action對應的int值
switch (ev.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
mInitialPoint = new PointF(ev.getX(), ev.getY());
// private static native long nativeCopy(long destNativePtr, long sourceNativePtr,boolean keepHistory); 該方法是 MotionEvent.obtainNoHistory(ev);調用最終通過這個nativeCopy方法復制一個event,但是不完全復制
mPendingDownMotionEvent = MotionEvent.obtainNoHistory(ev);
mDownMotionEventPended = true;
mIntercepting = mTouchInterceptionListener.shouldInterceptTouchEvent(ev, false, 0, 0);
mBeganFromDownMotionEvent = mIntercepting;
mChildrenEventsCanceled = false;
return mIntercepting;
case MotionEvent.ACTION_MOVE:
// 避免mInitialPoint沒有被初始化拋出異常
if (mInitialPoint == null) {
mInitialPoint = new PointF(ev.getX(), ev.getY());
}
//計算出滑動的距離以便於接口回調所需
float diffX = ev.getX() - mInitialPoint.x;
float diffY = ev.getY() - mInitialPoint.y;
mIntercepting = mTouchInterceptionListener.shouldInterceptTouchEvent(ev, true, diffX, diffY);
return mIntercepting;
}
return false;
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
if (mTouchInterceptionListener != null) {
switch (ev.getActionMasked()) {//如果touch事件攔截監聽器存在,並且需要攔截,會執行相應的攔截回調以及根據條件選擇自定義的事件分發
case MotionEvent.ACTION_DOWN:
if (mIntercepting) {
mTouchInterceptionListener.onDownMotionEvent(ev);
duplicateTouchEventForChildren(ev);
return true;
}
break;
case MotionEvent.ACTION_MOVE:
//...........................
}
private MotionEvent obtainMotionEvent(MotionEvent base, int action) {
MotionEvent ev = MotionEvent.obtainNoHistory(base);
ev.setAction(action);
return ev;
}
/**
* 重復的子視圖的觸摸事件。
* 我們想分發一個滑動事件給子視圖,但是調用dispatchTouchEvent()會導致StackOverflowError。
* 因此自己定義了一個事件分發。
*/
private void duplicateTouchEventForChildren(MotionEvent ev, MotionEvent... pendingEvents) {
//................
consumed |= childView.dispatchTouchEvent(event);
if (consumed) {
break;
}
}
}
}
}
Scrollable該接口為滑動控件提供相應的api
/**
* 該接口為滑動控件提供相應的api
*/
public interface Scrollable {
/**
* 設置滑動的回調監聽這個方法啊已經過時了,已經有了更好的方法
*/
@Deprecated
void setScrollViewCallbacks(ObservableScrollViewCallbacks listener);
/**
* 添加一個滑動事件的回調監聽
*/
void addScrollViewCallbacks(ObservableScrollViewCallbacks listener);
/**
* 移除滑動回調監聽器
*/
void removeScrollViewCallbacks(ObservableScrollViewCallbacks listener);
/**
* 清楚所有滑動監聽器
*/
void clearScrollViewCallbacks();
/**
* 垂直滾動到指定位置
* @param y Vertical position to scroll to.
*/
void scrollVerticallyTo(int y);
/**
*過去當前視圖位置所對應的Y值
*
* @return Current Y pixel.
*/
int getCurrentScrollY();
/**
* 設置觸摸事件的父布局視圖ViewGroup
*
* @param viewGroup ViewGroup object to dispatch motion events.
*/
void setTouchInterceptionViewGroup(ViewGroup viewGroup);
}
ObservableScrollViewCallbacks為滑動控件提供回調函數.
/**
* 為滑動控件提供回調函數.
*/
public interface ObservableScrollViewCallbacks {
/**
* 如果發生滑動變化時會回調該函數,如果你初始化視圖時想回調該方法需要手動調用
*
* @param scrollY 在Y軸滾動位置。
* @param firstScroll 是否是第一次(剛開始)滑動
* @param dragging 當前視圖是否是因為拖拽而產生滑動
*/
void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging);
/**
* 向下滑動事件回調函數
*/
void onDownMotionEvent();
/**
* 拖拽結束或者取消滑動
*
* @param scrollState 顯示滑動的方向
*/
void onUpOrCancelMotionEvent(ScrollState scrollState);
}
該庫提供的自定義控件如下圖都實現了Scollable,內部具體實現都差不多,粗略看一遍就可以開始我們的實踐了。這裡就以ObserableScrollView為例粗略過一遍
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
//是否有監聽器(set 以及add的監聽器)
if (hasNoCallbacks()) {
return;
}
mScrollY = t;
dispatchOnScrollChanged(t, mFirstScroll, mDragging);
if (mFirstScroll) {
mFirstScroll = false;
}
// 判斷滑動方向設置枚舉ScrollState
if (mPrevScrollY < t) {
mScrollState = ScrollState.UP;
} else if (t < mPrevScrollY) {
mScrollState = ScrollState.DOWN;
}
mPrevScrollY = t;
}
上裡代碼塊關聯的回調以及相關參數參考TouchEvent相關方法的參數初始化,例如mDragging,回調監聽器參考了源碼的寫法,讓setListener的寫法過時,采用add的方式,具體看代碼塊(源碼中ViewPager有這樣的寫法,好處不言而喻)
public class ObservableScrollView extends ScrollView implements Scrollable {
private ObservableScrollViewCallbacks mCallbacks;
private List mCallbackCollection;
@Override
public void setScrollViewCallbacks(ObservableScrollViewCallbacks listener) {
mCallbacks = listener;
}
@Override
public void addScrollViewCallbacks(ObservableScrollViewCallbacks listener) {
if (mCallbackCollection == null) {
mCallbackCollection = new ArrayList<>();
}
mCallbackCollection.add(listener);
}
@Override
public void removeScrollViewCallbacks(ObservableScrollViewCallbacks listener) {
if (mCallbackCollection != null) {
mCallbackCollection.remove(listener);
}
}
@Override
public void clearScrollViewCallbacks() {
if (mCallbackCollection != null) {
mCallbackCollection.clear();
}
}
ViewGroup事件判斷是否攔截以及子View的事件分發
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (hasNoCallbacks()) {
return super.onInterceptTouchEvent(ev);
}
switch (ev.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
mFirstScroll = mDragging = true;
dispatchOnDownMotionEvent();
break;
}
return super.onInterceptTouchEvent(ev);
}
private void dispatchOnDownMotionEvent() {
if (mCallbacks != null) {
mCallbacks.onDownMotionEvent();
}
if (mCallbackCollection != null) {
for (int i = 0; i < mCallbackCollection.size(); i++) {
ObservableScrollViewCallbacks callbacks = mCallbackCollection.get(i);
callbacks.onDownMotionEvent();
}
}
}
onTouchEvent我也只了解個大概說不出什麼來,自己慢慢看吧。下面開始我們的實戰ActionBar+Observable系列控件,線上效果圖
以上效果根據我先有demo CityList修改而成(cityList庫分析地址http://blog.csdn.net/analyzesystem/article/details/46890905)上圖效果關於CitiyList部分就不細說了,核心代碼部分在於Activity實現ObservableScrollViewCallbacks,在回調方法onUpOrCancelMotionEvent方法做ActionBar的動態隱藏顯示。
@Override
public void onUpOrCancelMotionEvent(ScrollState scrollState) {
ActionBar ab = getSupportActionBar();
if (ab == null) {
return;
}
if (scrollState == ScrollState.UP) {
if (ab.isShowing()) {
ab.hide();
}
} else if (scrollState == ScrollState.DOWN) {
if (!ab.isShowing()) {
ab.show();
}
}
}
ActionBar的hide方法和show方法具體是怎麼實現的呢,根進源碼發現ActionBar只是定義了抽象方法,具體實現在那麼呢,在搜索資料過程中發現一篇關於ActionBar源碼分析的文章:http://blog.csdn.net/hello__zero/article/details/30517245?utm_source=tuicool&utm_medium=referral,根據文章提示進到ActionBarImpl源碼(http://code1.okbase.net/codefile/ActionBarImpl.java_2014110527698_13.htm)找到hide和show的具體實現
void show(boolean markHiddenBeforeMode) {
if (mCurrentShowAnim != null) {
mCurrentShowAnim.end();
}
if (mContainerView.getVisibility() == View.VISIBLE) {
if (markHiddenBeforeMode) mWasHiddenBeforeMode = false;
return;
}
mContainerView.setVisibility(View.VISIBLE);
if (mShowHideAnimationEnabled) {
mContainerView.setAlpha(0);
AnimatorSet anim = new AnimatorSet();
AnimatorSet.Builder b = anim.play(ObjectAnimator.ofFloat(mContainerView, "alpha", 1));
if (mContentView != null) {
b.with(ObjectAnimator.ofFloat(mContentView, "translationY",
-mContainerView.getHeight(), 0));
mContainerView.setTranslationY(-mContainerView.getHeight());
b.with(ObjectAnimator.ofFloat(mContainerView, "translationY", 0));
}
if (mSplitView != null && mContextDisplayMode == CONTEXT_DISPLAY_SPLIT) {
mSplitView.setAlpha(0);
mSplitView.setVisibility(View.VISIBLE);
b.with(ObjectAnimator.ofFloat(mSplitView, "alpha", 1));
}
anim.addListener(mShowListener);
mCurrentShowAnim = anim;
anim.start();
} else {
mContainerView.setAlpha(1);
mContainerView.setTranslationY(0);
mShowListener.onAnimationEnd(null);
}
}
@Override
public void hide() {
if (mCurrentShowAnim != null) {
mCurrentShowAnim.end();
}
if (mContainerView.getVisibility() == View.GONE) {
return;
}
if (mShowHideAnimationEnabled) {
mContainerView.setAlpha(1);
mContainerView.setTransitioning(true);
AnimatorSet anim = new AnimatorSet();
AnimatorSet.Builder b = anim.play(ObjectAnimator.ofFloat(mContainerView, "alpha", 0));
if (mContentView != null) {
b.with(ObjectAnimator.ofFloat(mContentView, "translationY",
0, -mContainerView.getHeight()));
b.with(ObjectAnimator.ofFloat(mContainerView, "translationY",
-mContainerView.getHeight()));
}
if (mSplitView != null && mSplitView.getVisibility() == View.VISIBLE) {
mSplitView.setAlpha(1);
b.with(ObjectAnimator.ofFloat(mSplitView, "alpha", 0));
}
anim.addListener(mHideListener);
mCurrentShowAnim = anim;
anim.start();
} else {
mHideListener.onAnimationEnd(null);
}
}
ObjectAnimator實現動畫效果的位移隱藏以及透明度的變化(如果你對上裡代表表示看不太懂,我想你應該補腦動畫相關用到的坐標系了),以上是通過ActionBar配合實現
Google即將發布的Android7.0的預覽版Android_N為我們增加了許多新的特性,其中包括多窗口的支持、通知欄支持直接回復、網絡數據節省開關、以及新的DOZE
先上效果圖: Layout文件: 首先介紹兩種把資源裡的drawable轉成bitmap的方式 第一種: Bitma
Socket是基於Tcp的鏈接,適用於長鏈接Socke通訊需要客戶端和服務器,客戶端我們在android上編寫,而服務器則選擇在eclipse上編寫上效果圖;eclips
屬性名 用途 hibernate.dialect 一個HibernateDialect類名允許Hibernate針對特定的關系數據庫生成優化的SQL.取值