編輯:關於Android編程
啥也不說,先來個預覽圖,雖然有點卡:
如果覺得圖比較卡,可以搜一下DragTopLayout,git上有一個用的比較廣的,附地址:
https://github.com/chenupt/DragTopLayout
這個DragTopLayout比較復雜,而且存在一些bug,比如頭部的控件高度是變化的情況下,會存在一些問題,使用時還要借助EventBus,比較麻煩。
綜合以上原因,於是乎,自己寫了一個,只有一個類,使用非常簡單。
下面先介紹一下基本用法:
這裡用了我的另一篇博客裡的ExpandableTextView
,感興趣的可以去參考一下
Activity代碼:
public class MainActivity extends Activity {
private DragTopLayout drag;
private ListView lv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
drag = (DragTopLayout) findViewById(R.id.drag);
lv = (ListView) findViewById(R.id.lv);
// 設置滾動的view,因為內容不居中可能有好幾個view,所以要手動指定誰是可滾動的布局
// 我這裡是ListView,還可以是ScrollView等
drag.setTargetView(lv);
lv.setAdapter(new BaseAdapter() {
@Override
public int getCount() {
return 50;
}
@Override
public Object getItem(int position) {
return null;
}
@Override
public long getItemId(int position) {
return 0;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
TextView t = new TextView(MainActivity.this);
t.setText("==================== " +position);
return t;
}
});
}
}
整體使用就是這樣,so easy。
下面是DragTopLayout的代碼,非常簡潔
代碼中的注釋還是比較詳細的,單獨說幾點比較關鍵的:
canChildScrollUp()方法是判斷可滾動View是否能向上滾動,例如ListView是否已經滾動到頂部,是否可以繼續向上滾動,這個方法參考了SwipeRefreshLayout中的判定,是非常權威滴 requestDisallowInterceptTouchEvent()方法也是參考的SwipeRefreshLayout中的寫法,至於為什麼,參考SwipeRefreshLayout的事件處理。
最後一點就是
onTouchEvent()的
MotionEvent.ACTION_MOVE事件中的距離計算可能有點不好理解,這個就需要你對自定義View了解的比較深入才能明白了,如果說的比較透徹可能篇幅較大,各位童鞋單獨找資料學習吧 = =
最後一點,這種效果其實可以借助
CoordinatorLayout來實現。。。。。。
public class DragTopLayout extends FrameLayout {
public DragTopLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
public DragTopLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public DragTopLayout(Context context) {
this(context, null);
}
private void init() {
mScroller = new Scroller(getContext().getApplicationContext());
}
/**
* 當前頭部可見還是滾出屏幕,true為滾出屏幕
*/
private boolean mCollapsed = false;
/**
* 頭部高度
*/
private int mHeadHeight;
/**
* 滾動輔助
*/
private Scroller mScroller;
/**
* 速度計算,每次用完要recycle
*/
private VelocityTracker velocityTracker;
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
measureChildren(widthMeasureSpec, heightMeasureSpec);
mHeadHeight = getChildAt(0).getMeasuredHeight();
}
@Override
protected void onLayout(boolean changed, int left, int top, int right,
int bottom) {
View v1 = getChildAt(0);
v1.layout(0, 0, v1.getMeasuredWidth(), v1.getMeasuredHeight());
View v2 = getChildAt(1);
v2.layout(0, v1.getMeasuredHeight(), getMeasuredWidth(),
getMeasuredHeight() + mHeadHeight);
}
/**
* 按下點,滑動過程中的上一個點
*/
private PointF mDownPoint = new PointF();
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mDownPoint.set(event.getX(), event.getY());
break;
case MotionEvent.ACTION_MOVE:
float y = event.getY();
// Y方向需要scrollBy的距離,正值表示需要向上滾動
float scrollByDelt = -y + mDownPoint.y;
// 假如按照計算的距離偏移後的偏移量,即getScrollY()的值
float realDelt = getScrollY() + scrollByDelt;
Log.e("lzw", "------> " + scrollByDelt + " " + realDelt);
if (realDelt < 0) { // 表示按照實際的手指滑動距離向下移動的話太大,則直接計算出剛好頭部顯示出來的realDelt
scrollByDelt = 0 - getScrollY();
} else if (realDelt > mHeadHeight) { // 同樣表示實際距離太大,計算出合適的距離
scrollByDelt = mHeadHeight - getScrollY();
}
scrollBy(0, (int) scrollByDelt);
mDownPoint.set(event.getX(), y);
break;
case MotionEvent.ACTION_UP:
mDownPoint.set(0, 0);
checkPosition();
break;
}
return true;
}
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
Log.e("lzw", "onInterceptTouchEvent canChildScrollUp " + canChildScrollUp());
if (canChildScrollUp()) { // 能向上滾動,一定是滾動view處理事件
return false;
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mDownPoint.set(event.getX(), event.getY());
return false;
case MotionEvent.ACTION_MOVE:
// 橫向滾動大於縱向,也不響應
if (Math.abs(event.getX() - mDownPoint.x) > Math.abs(event.getY()
- mDownPoint.y)) {
mDownPoint.set(event.getX(), event.getY());
return super.onInterceptTouchEvent(event);
}
// 在向下移動
if (event.getY() > mDownPoint.y) {
mDownPoint.set(event.getX(), event.getY());
if (mCollapsed) { // 頭部不可見了,向下滾動需要攔截
return true;
} else {
return super.onInterceptTouchEvent(event);
}
}
// 在向上移動
if (event.getY() < mDownPoint.y) {
mDownPoint.set(event.getX(), event.getY());
if (mCollapsed) { // 頭部滾出屏幕,不攔截
return super.onInterceptTouchEvent(event);
} else {
return true;
}
}
mDownPoint.set(event.getX(), event.getY());
case MotionEvent.ACTION_UP:
// 檢查頭部是否移除去
mCollapsed = getScrollY() >= mHeadHeight;
mDownPoint.set(event.getX(), event.getY());
return super.onInterceptTouchEvent(event);
}
return true;
}
@Override
public void requestDisallowInterceptTouchEvent(boolean b) {
// if this is a List < L or another view that doesn't support nested
// scrolling, ignore this request so that the vertical scroll event
// isn't stolen
if ((android.os.Build.VERSION.SDK_INT < 21 && mTarget instanceof AbsListView)
|| (mTarget != null && !ViewCompat.isNestedScrollingEnabled(mTarget))) {
// Nope.
} else {
super.requestDisallowInterceptTouchEvent(b);
}
}
/**
* @return Whether it is possible for the child view of this layout to
* scroll up. Override this if the child view is a custom view.
*/
public boolean canChildScrollUp() {
if (android.os.Build.VERSION.SDK_INT < 14) {
if (mTarget instanceof AbsListView) {
final AbsListView absListView = (AbsListView) mTarget;
return absListView.getChildCount() > 0
&& (absListView.getFirstVisiblePosition() > 0 || absListView
.getChildAt(0).getTop() < absListView
.getPaddingTop());
} else {
return ViewCompat.canScrollVertically(mTarget, -1)
|| mTarget.getScrollY() > 0;
}
} else {
return ViewCompat.canScrollVertically(mTarget, -1);
}
}
/**
* 檢查是否需要關閉或者打開
*/
private void checkPosition() {
// 移出去的大小,不能直接在if裡面用,否則返回值不正確
int opOffset = getScrollY();
if (opOffset < (mHeadHeight / 2)) {
open();
} else {
close();
}
}
/**
* 向上移動,隱藏頭部
*/
private void close() {
mCollapsed = true;
mScroller.startScroll(0, getScrollY(), 0, mHeadHeight - getScrollY());
invalidate();
}
/**
* 向下移動,頭部出現
*/
private void open() {
mCollapsed = false;
mScroller.startScroll(0, getScrollY(), 0, -getScrollY());
invalidate();
}
@Override
public void computeScroll() {
// 返回值為boolean,true說明滾動尚未完成,false說明滾動已經完成。
if (mScroller.computeScrollOffset()) {
// 移動位置
scrollTo(0, mScroller.getCurrY());
invalidate();
}
}
/**
* 可滾動view
*/
private View mTarget;
/**
* 設置可滾動view
*/
public void setTargetView(View v) {
mTarget = v;
}
}
以上,應該沒什麼大的bug,有bug大家留言 -_-
OpenCV中強大的Mat類型大家已經比較熟悉了。這裡梳理一些在工程中其他經常用到的幾種基本數據類型。包括:VecScalarPointSizeRectRotatedRe
隨著玩微信的人越來越大,問題就出現了,很多人經常發些假的消息或者頻繁加陌生人為好人,結果被人舉報,那麼微信被舉報怎麼解除呢?下面小編就教大家微信被舉報的解除
我們有時候會遇到這麼一個情況。就是我在一個ListView裡面需要顯示的東西其實是有種類之分的。比如我要分冬天,夏天,秋天,春天,然後在這每個季節下面再去加載各自的條目數
本章使用Intent,回傳需要的結果; 流程: 主頁讀取用戶名並顯示出來。 1 啟動主頁圖: 2 啟動第二個activity 3 輸入用戶名: 4 回傳到主頁