其实网上类似的实现已经很多了,原理也并不难,只是网上各种demo运行下来,多少都有一些问题。折腾了半天,决定自己实现一个。

首先我们看看实现效果:

这里写图片描述

对比网上各类demo,这次要实现的主要表现在以下几点:

1.侧滑显示抽屉view

2.侧滑抽屉隐藏view控件点击事件

3.单击任意item隐藏显示的抽屉view

4.滑动list隐藏显示的抽屉view

5.增加SwipeLayout点击事件和Swipe touch事件判断处理

6.优化快速划开多个抽屉隐藏view时多个SwipeLayout滑动状态判断处理,仅显示最后一个滑动的抽屉隐藏view,隐藏前面所有打开的抽屉view(快速滑动时,可能存在多个抽屉view打开情况,网上找的几个demo主要问题都集中在这一块)

实现原理

其实单就一个SwipeLayout的实现原理来讲的话,还是很简单的,实际上单个SwipeLayout隐藏抽屉状态时,应该是这样的:

这里写图片描述

也就是说,最初的隐藏状态,实际上是将hide view区域layout到conten view的右边,达到隐藏效果,而后显示则是根据拖拽的x值变化来动态的layout 2个view,从而达到一个滑动抽屉效果。

当然,直接重写view的onTouchEvent来动态的layout 2个view是可以实现我们需要的效果的,但是有更好的方法来实现,就是同过ViewDragHelper。

ViewDragHelper是google官方提供的一个专门用于手势分析处理的类,关于ViewDragHelper的基本使用,网上有一大堆的资源。具体的ViewDragHelper介绍以及基本使用方法,本文就不重复造轮子了,此处推荐鸿洋大神的一篇微博:http://blog.csdn.net/lmj623565791/article/details/46858663。

具体实现

下面我们开始具体的实现。

布局比较简单,这里就不贴代码了,最后会贴上本demo的完整代码地址。

首先我们实现一个继承FrameLayout的自定义SwipeLauout,重写onFinishInflate方法:

这里我们只允许SwipeLayout设置2个子View,ContentLayout是继承LinearLayout的自定义layout,后面会讲到这个,此处先略过;

@Override

protected void onFinishInflate() {

super.onFinishInflate();

if (getChildCount() != 2) {

throw new IllegalStateException("Must 2 views in SwipeLayout");

}

contentView = getChildAt(0);

hideView = getChildAt(1);

if (contentView instanceof ContentLayout)

((ContentLayout) contentView).setSwipeLayout(this);

else {

throw new IllegalStateException("content view must be an instanceof FrontLayout");

}

}

接着重写onSizeChanged,onLayout,onInterceptTouchEvent方法:

@Override

protected void onSizeChanged(int w, int h, int oldw, int oldh) {

super.onSizeChanged(w, h, oldw, oldh);

hideViewHeight = hideView.getMeasuredHeight();

hideViewWidth = hideView.getMeasuredWidth();

contentWidth = contentView.getMeasuredWidth();

}

@Override

protected void onLayout(boolean changed, int left, int top, int right,

int bottom) {

// super.onLayout(changed, left, top, right, bottom);

contentView.layout(0, 0, contentWidth, hideViewHeight);

hideView.layout(contentView.getRight(), 0, contentView.getRight()

+ hideViewWidth, hideViewHeight);

}

@Override

public boolean onInterceptTouchEvent(MotionEvent ev) {

boolean result = viewDragHelper.shouldInterceptTouchEvent(ev);

// Log.e("SwipeLayout", "-----onInterceptTouchEvent-----");

return result;

}

然后是比较关键的,重写onTouchEvent方法以及ViewDragHelper.Callback回调,我们定了一个enum来判断SwipeLayout的三种状态。在onViewPositionChanged中,有2种方法实现content view和hide view的伴随移动,一种是直接offset view的横向变化量,还有一种就是直接通过layout的方式,两种方式都可以。

public enum SwipeState {

Open, Swiping, Close;

}

@Override

public boolean onTouchEvent(MotionEvent event) {

// Log.e("SwipeLayout", "-----onTouchEvent-----");

switch (event.getAction()) {

case MotionEvent.ACTION_DOWN:

downX = event.getX();

downY = event.getY();

break;

case MotionEvent.ACTION_MOVE:

// 1.获取x和y方向移动的距离

float moveX = event.getX();

float moveY = event.getY();

float delatX = moveX - downX;// x方向移动的距离

float delatY = moveY - downY;// y方向移动的距离

if (Math.abs(delatX) > Math.abs(delatY)) {

// 表示移动是偏向于水平方向,那么应该SwipeLayout应该处理,请求listview不要拦截

this.requestDisallowInterceptTouchEvent(true);

}

// 更新downX,downY

downX = moveX;

downY = moveY;

break;

case MotionEvent.ACTION_UP:

break;

}

viewDragHelper.processTouchEvent(event);

return true;

}

private ViewDragHelper.Callback callback = new ViewDragHelper.Callback() {

@Override

public boolean tryCaptureView(View child, int pointerId) {

return child == contentView || child == hideView;

}

@Override

public int getViewHorizontalDragRange(View child) {

return hideViewWidth;

}

@Override

public int clampViewPositionHorizontal(View child, int left, int dx) {

if (child == contentView) {

if (left > 0)

left = 0;

if (left < -hideViewWidth)

left = -hideViewWidth;

} else if (child == hideView) {

if (left > contentWidth)

left = contentWidth;

if (left < (contentWidth - hideViewWidth))

left = contentWidth - hideViewWidth;

}

return left;

}

@Override

public void onViewPositionChanged(View changedView, int left, int top,

int dx, int dy) {

super.onViewPositionChanged(changedView, left, top, dx, dy);

if (changedView == contentView) {

// 如果手指滑动deleteView,那么也要讲横向变化量dx设置给contentView

hideView.offsetLeftAndRight(dx);

} else if (changedView == hideView) {

// 如果手指滑动contentView,那么也要讲横向变化量dx设置给deleteView

contentView.offsetLeftAndRight(dx);

}

// if (changedView == contentView) {

// // 手动移动deleteView

// hideView.layout(hideView.getLeft() + dx,

// hideView.getTop() + dy, hideView.getRight() + dx,

// hideView.getBottom() + dy);

// } else if (hideView == changedView) {

// // 手动移动contentView

// contentView.layout(contentView.getLeft() + dx,

// contentView.getTop() + dy, contentView.getRight() + dx,

// contentView.getBottom() + dy);

// }

//实时更新当前状态

updateSwipeStates();

invalidate();

}

@Override

public void onViewReleased(View releasedChild, float xvel, float yvel) {

super.onViewReleased(releasedChild, xvel, yvel);

//根据用户滑动速度处理开关

//xvel: x方向滑动速度

//yvel: y方向滑动速度

// Log.e("tag", "currentState = " + currentState);

// Log.e("tag", "xvel = " + xvel);

if (xvel < -200 && currentState != SwipeState.Open) {

open();

return;

} else if (xvel > 200 && currentState != SwipeState.Close) {

close();

return;

}

if (contentView.getLeft() < -hideViewWidth / 2) {

// 打开

open();

} else {

// 关闭

close();

}

}

};

open(),close()实现

public void open() {

open(true);

}

public void close() {

close(true);

}

/**

* 打开的方法

*

* @param isSmooth 是否通过缓冲动画的形式设定view的位置

*/

public void open(boolean isSmooth) {

if (isSmooth) {

viewDragHelper.smoothSlideViewTo(contentView, -hideViewWidth,

contentView.getTop());

ViewCompat.postInvalidateOnAnimation(SwipeLayout.this);

} else {

contentView.offsetLeftAndRight(-hideViewWidth);//直接偏移View的位置

hideView.offsetLeftAndRight(-hideViewWidth);//直接偏移View的位置

// contentView.layout(-hideViewWidth, 0, contentWidth - hideViewWidth, hideViewHeight);//直接通过坐标摆放

// hideView.layout(contentView.getRight(), 0, hideViewWidth, hideViewHeight);//直接通过坐标摆放

invalidate();

}

}

/**

* 关闭的方法

*

* @param isSmooth true:通过缓冲动画的形式设定view的位置

* false:直接设定view的位置

*/

public void close(boolean isSmooth) {

if (isSmooth) {

viewDragHelper.smoothSlideViewTo(contentView, 0, contentView.getTop());

ViewCompat.postInvalidateOnAnimation(SwipeLayout.this);

} else {

contentView.offsetLeftAndRight(hideViewWidth);

hideView.offsetLeftAndRight(hideViewWidth);

invalidate();

//contentView.layout(0, 0, contentWidth, hideViewHeight);//直接通过坐标摆放

//hideView.layout(contentView.getRight(), 0, hideViewWidth, hideViewHeight);//直接通过坐标摆放

}

}

此上基本实现了单个SwipeLayout的抽屉滑动效果,但是将此SwipeLayout作为一个item布局设置给一个listView的时候,还需要做许多的判断。

由于listView的重用机制,我们这里并未针对listview做任何处理,所以一旦有一个item的SwipeLayout的状态是打开状态,不可避免的其它也必然有几个是打开状态,所以我们这里需要根据检测listView的滑动,当listView滑动时,关闭SwipeLayout。既然需要在外部控制SwipeLayout的开关,我们先定义一个SwipeLayoutManager用于管理SwipeLayout的控制。

public class SwipeLayoutManager {

//记录打开的SwipeLayout集合

private HashSet mUnClosedSwipeLayouts = new HashSet();

private SwipeLayoutManager() {

}

private static SwipeLayoutManager mInstance = new SwipeLayoutManager();

public static SwipeLayoutManager getInstance() {

return mInstance;

}

/**

* 将一个没有关闭的SwipeLayout加入集合

* @param layout

*/

public void add(SwipeLayout layout) {

mUnClosedSwipeLayouts.add(layout);

}

/**

* 将一个没有关闭的SwipeLayout移出集合

* @param layout

*/

public void remove(SwipeLayout layout){

mUnClosedSwipeLayouts.remove(layout);

}

/**

* 关闭已经打开的SwipeLayout

*/

public void closeUnCloseSwipeLayout() {

if(mUnClosedSwipeLayouts.size() == 0){

return;

}

for(SwipeLayout l : mUnClosedSwipeLayouts){

l.close(true);

}

mUnClosedSwipeLayouts.clear();

}

/**

* 关闭已经打开的SwipeLayout

*/

public void closeUnCloseSwipeLayout(boolean isSmooth) {

if(mUnClosedSwipeLayouts.size() == 0){

return;

}

for(SwipeLayout l : mUnClosedSwipeLayouts){

l.close(isSmooth);

}

mUnClosedSwipeLayouts.clear();

}

}

这样就可以监听listView的滑动,然后在listView滑动的时候,关闭所有的抽屉View。

listView.setOnScrollListener(new OnScrollListener() {

@Override

public void onScrollStateChanged(AbsListView view, int scrollState) {

swipeLayoutManager.closeUnCloseSwipeLayout();

}

@Override

public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {

}

});

考虑到大多数时候,在我们打开抽屉View和关闭抽屉View的时候,外部需要知道SwipeLayout的状态值,所以我们需要在SwipeLayout中增加几个接口,告诉外部当前SwipeLayout的状态值:

SwipeLayout.java

----------------

private void updateSwipeStates() {

SwipeState lastSwipeState = currentState;

SwipeState swipeState = getCurrentState();

if (listener == null) {

try {

throw new Exception("please setOnSwipeStateChangeListener first!");

} catch (Exception e) {

e.printStackTrace();

}

return;

}

if (swipeState != currentState) {

currentState = swipeState;

if (currentState == SwipeState.Open) {

listener.onOpen(this);

// 当前的Swipelayout已经打开,需要让Manager记录

swipeLayoutManager.add(this);

} else if (currentState == SwipeState.Close) {

listener.onClose(this);

// 说明当前的SwipeLayout已经关闭,需要让Manager移除

swipeLayoutManager.remove(this);

} else if (currentState == SwipeState.Swiping) {

if (lastSwipeState == SwipeState.Open) {

listener.onStartClose(this);

} else if (lastSwipeState == SwipeState.Close) {

listener.onStartOpen(this);

//hideView准备显示之前,先将之前打开的的SwipeLayout全部关闭

swipeLayoutManager.closeUnCloseSwipeLayout();

swipeLayoutManager.add(this);

}

}

} else {

currentState = swipeState;

}

}

/**

* 获取当前控件状态

*

* @return

*/

public SwipeState getCurrentState() {

int left = contentView.getLeft();

// Log.e("tag", "contentView.getLeft() = " + left);

// Log.e("tag", "hideViewWidth = " + hideViewWidth);

if (left == 0) {

return SwipeState.Close;

}

if (left == -hideViewWidth) {

return SwipeState.Open;

}

return SwipeState.Swiping;

}

private OnSwipeStateChangeListener listener;

public void setOnSwipeStateChangeListener(

OnSwipeStateChangeListener listener) {

this.listener = listener;

}

public View getContentView() {

return contentView;

}

public interface OnSwipeStateChangeListener {

void onOpen(SwipeLayout swipeLayout);

void onClose(SwipeLayout swipeLayout);

void onStartOpen(SwipeLayout swipeLayout);

void onStartClose(SwipeLayout swipeLayout);

}

然后接下来是写一个为listView设置的SwipeAdapter

SwipeAdapter.java

------------

public class SwipeAdapter extends BaseAdapter implements OnSwipeStateChangeListener {

private Context mContext;

private List list;

private MyClickListener myClickListener;

private SwipeLayoutManager swipeLayoutManager;

public SwipeAdapter(Context mContext) {

super();

this.mContext = mContext;

init();

}

private void init() {

myClickListener = new MyClickListener();

swipeLayoutManager = SwipeLayoutManager.getInstance();

}

public void setList(List list){

this.list = list;

notifyDataSetChanged();

}

@Override

public int getCount() {

return list.size();

}

@Override

public Object getItem(int position) {

return list.get(position);

}

@Override

public long getItemId(int position) {

return position;

}

@Override

public View getView(final int position, View convertView, ViewGroup parent) {

if (convertView == null) {

convertView = UIUtils.inflate(R.layout.list_item_swipe);

}

ViewHolder holder = ViewHolder.getHolder(convertView);

holder.tv_content.setText(list.get(position));

holder.tv_overhead.setOnClickListener(myClickListener);

holder.tv_overhead.setTag(position);

holder.tv_delete.setOnClickListener(myClickListener);

holder.tv_delete.setTag(position);

holder.sv_layout.setOnSwipeStateChangeListener(this);

holder.sv_layout.setTag(position);

holder.sv_layout.getContentView().setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

ToastUtils.showToast("item click : " + position);

swipeLayoutManager.closeUnCloseSwipeLayout();

}

});

return convertView;

}

static class ViewHolder {

TextView tv_content, tv_overhead, tv_delete;

SwipeLayout sv_layout;

public ViewHolder(View convertView) {

tv_content = (TextView) convertView.findViewById(R.id.tv_content);

tv_overhead = (TextView) convertView.findViewById(R.id.tv_overhead);

tv_delete = (TextView) convertView.findViewById(R.id.tv_delete);

sv_layout = (SwipeLayout) convertView.findViewById(R.id.sv_layout);

}

public static ViewHolder getHolder(View convertView) {

ViewHolder holder = (ViewHolder) convertView.getTag();

if (holder == null) {

holder = new ViewHolder(convertView);

convertView.setTag(holder);

}

return holder;

}

}

class MyClickListener implements View.OnClickListener {

@Override

public void onClick(View v) {

Integer position = (Integer) v.getTag();

switch (v.getId()) {

case R.id.tv_overhead:

//ToastUtils.showToast("position : " + position + " overhead is clicked.");

}

break;

case R.id.tv_delete:

//ToastUtils.showToast("position : " + position + " delete is clicked.");

}

break;

default:

break;

}

}

}

@Override

public void onOpen(SwipeLayout swipeLayout) {

//ToastUtils.showToast(swipeLayout.getTag() + "onOpen.");

}

@Override

public void onClose(SwipeLayout swipeLayout) {

//ToastUtils.showToast(swipeLayout.getTag() + "onClose.");

}

@Override

public void onStartOpen(SwipeLayout swipeLayout) {

// ToastUtils.showToast("onStartOpen.");

}

@Override

public void onStartClose(SwipeLayout swipeLayout) {

// ToastUtils.showToast("onStartClose.");

}

}

此时已经基本实现了我们需要的大部分功能了,但是当我们滑动的时候,又发现新的问题,我们的SwipeLayout和listview滑动判断有问题。由于前面我们仅仅是将touch拦截事件简简单单的丢给了viewDragHelper.shouldInterceptTouchEvent(ev)来处理,导致SwipeLayout和listview拦截touch事件时的处理存在一定的问题,这里我们要提到一个知识点:Android view事件的传递。

(1)首先由Activity分发,分发给根View,也就是DecorView(DecorView为整个Window界面的最顶层View)

(2)然后由根View分发到子的View

view事件拦截如下图所示:

这里写图片描述

view事件的消费如下图所示:

这里写图片描述

注:以上2张图借鉴网上总结的比较经典的图

所以这里我们就要谈到一开始出现的ContentLayout,主要重写了onInterceptTouchEvent和onTouchEvent。

public class ContentLayout extends LinearLayout {

SwipeLayoutInterface mISwipeLayout;

public ContentLayout(Context context) {

super(context);

}

public ContentLayout(Context context, AttributeSet attrs) {

super(context, attrs);

}

public ContentLayout(Context context, AttributeSet attrs, int defStyleAttr) {

super(context, attrs, defStyleAttr);

}

public void setSwipeLayout(SwipeLayoutInterface iSwipeLayout) {

this.mISwipeLayout = iSwipeLayout;

}

@Override

public boolean onInterceptTouchEvent(MotionEvent ev) {

// Log.e("ContentLayout", "-----onInterceptTouchEvent-----");

if (mISwipeLayout.getCurrentState() == SwipeState.Close) {

return super.onInterceptTouchEvent(ev);

} else {

return true;

}

}

@Override

public boolean onTouchEvent(MotionEvent ev) {

// Log.e("ContentLayout", "-----onTouchEvent-----");

if (mISwipeLayout.getCurrentState() == SwipeState.Close) {

return super.onTouchEvent(ev);

} else {

if (ev.getActionMasked() == MotionEvent.ACTION_UP) {

mISwipeLayout.close();

}

return true;

}

}

}

另外由于在ContentLayout中需要拿到父View SwipeLayout的开关状态以及控制SwipeLayout的关闭,因此在再写一个接口,用于ContentLayout获取SwipeLayout的开关状态以及更新SwipeLayout。

public interface SwipeLayoutInterface {

SwipeState getCurrentState();

void open();

void close();

}

然后接着的是完善SwipeLayout的onInterceptTouchEvent,我们在这里增加一个GestureDetectorCompat处理手势识别:

private void init(Context context) {

viewDragHelper = ViewDragHelper.create(this, callback);

mGestureDetector = new GestureDetectorCompat(context, mOnGestureListener);

swipeLayoutManager = SwipeLayoutManager.getInstance();

}

private SimpleOnGestureListener mOnGestureListener = new SimpleOnGestureListener() {

@Override

public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {

// 当横向移动距离大于等于纵向时,返回true

return Math.abs(distanceX) >= Math.abs(distanceY);

}

};

@Override

public boolean onInterceptTouchEvent(MotionEvent ev) {

boolean result = viewDragHelper.shouldInterceptTouchEvent(ev) & mGestureDetector.onTouchEvent(ev);

// Log.e("SwipeLayout", "-----onInterceptTouchEvent-----");

return result;

}

如此下来,整个View不管是上下拖动,还是SwipeLayout的开关滑动,都已经实现完成了。最后增加对应overhead,delete以及item的点击事件,此处完善SwipeAdapter的代码之后如下。

class MyClickListener implements View.OnClickListener {

@Override

public void onClick(View v) {

Integer position = (Integer) v.getTag();

switch (v.getId()) {

case R.id.tv_overhead:

//ToastUtils.showToast("position : " + position + " overhead is clicked.");

swipeLayoutManager.closeUnCloseSwipeLayout(false);

if(onSwipeControlListener != null){

onSwipeControlListener.onOverhead(position, list.get(position));

}

break;

case R.id.tv_delete:

//ToastUtils.showToast("position : " + position + " delete is clicked.");

swipeLayoutManager.closeUnCloseSwipeLayout(false);

if(onSwipeControlListener != null){

onSwipeControlListener.onDelete(position, list.get(position));

}

break;

default:

break;

}

}

}

private OnSwipeControlListener onSwipeControlListener;

public void setOnSwipeControlListener(OnSwipeControlListener onSwipeControlListener){

this.onSwipeControlListener = onSwipeControlListener;

}

/**

* overhead 和 delete点击事件接口

*/

public interface OnSwipeControlListener{

void onOverhead(int position, String itemTitle);

void onDelete(int position, String itemTitle);

}

最后贴上MainActivity代码,此处通过OnSwipeControlListener接口回调实现item的删除和置顶:

public class MainActivity extends Activity implements OnSwipeControlListener {

private ListView listView;

private List list = new ArrayList();

private SwipeLayoutManager swipeLayoutManager;

private SwipeAdapter swipeAdapter;

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

initData();

initView();

}

private void initData() {

for (int i = 0; i < 50; i++) {

list.add("content - " + i);

}

}

private void initView() {

swipeLayoutManager = SwipeLayoutManager.getInstance();

swipeAdapter = new SwipeAdapter(this);

swipeAdapter.setList(list);

listView = (ListView) findViewById(R.id.list_view);

listView.setAdapter(swipeAdapter);

listView.setOnScrollListener(new OnScrollListener() {

@Override

public void onScrollStateChanged(AbsListView view, int scrollState) {

swipeLayoutManager.closeUnCloseSwipeLayout();

}

@Override

public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {

}

});

swipeAdapter.setOnSwipeControlListener(this);

}

@Override

public void onOverhead(int position, String itemTitle) {

setItemOverhead(position, itemTitle);

}

@Override

public void onDelete(int position, String itemTitle) {

removeItem(position, itemTitle);

}

/**

* 设置item置顶

*

* @param position

* @param itemTitle

*/

private void setItemOverhead(int position, String itemTitle) {

// ToastUtils.showToast("position : " + position + " overhead.");

ToastUtils.showToast("overhead ---" + itemTitle + "--- success.");

String newTitle = itemTitle;

list.remove(position);//删除要置顶的item

list.add(0, newTitle);//根据adapter传来的Title数据在list 0位置插入title字符串,达到置顶效果

swipeAdapter.setList(list);//重新给Adapter设置list数据并更新

UIUtils.runOnUIThread(new Runnable() {

@Override

public void run() {

listView.setSelection(0);//listview选中第0项item

}

});

}

/**

* 删除item

*

* @param position

* @param itemTitle

*/

private void removeItem(int position, String itemTitle) {

// ToastUtils.showToast("position : " + position + " delete.");

ToastUtils.showToast("delete ---" + itemTitle + "--- success.");

list.remove(position);

swipeAdapter.setList(list);//重新给Adapter设置list数据并更新

}

}

至此整个demo基本完成,本次完成的功能基本能够直接放到项目中使用。其实最麻烦的地方就在于view的touch事件拦截和处理,不过将本demo的log打开看一下对比之后,也就能够理解整个传递过程了。

android中抽屉控件,Android自定义控件:类QQ抽屉效果相关推荐

  1. android listview 滑动条显示_第七十六回:Android中UI控件之RecyclerView基础

    各位看官们,大家好,上一回中咱们说的是Android中UI控件之ListView优化的例子,这一回咱们说的例子是UI控件之RecyclerView.闲话休提,言归正转.让我们一起Talk Androi ...

  2. 微信小程序手把手教你实现类似Android中ViewPager控件效果

    微信小程序手把手教你实现类似Android中ViewPager控件效果 前言 需求分析 头部TAB 滑动的内容部分 最终版本 尾巴 前言 在做Android开发的时候,ViewPager是开发者使用频 ...

  3. android中ListView控件onItemClick事件中获取listView传递的数据

    http://blog.csdn.net/aben_2005/article/details/6592205 本文转载自:android中ListView控件&&onItemClick ...

  4. Android中ExpandableListView控件基本使用

    本文採用一个Demo来展示Android中ExpandableListView控件的使用,如怎样在组/子ListView中绑定数据源.直接上代码例如以下: 程序结构图: layout文件夹下的 mai ...

  5. android通过代码设置铃声_第六十四回:Android中UI控件之SeekBar

    各位看官们,大家好,上一回中咱们说的是Android中UI控件之ProgressBar的例子,这一回咱们的例子是UI控件之SeekBar.闲话休提,言归正转.让我们一起Talk Android吧! 看 ...

  6. android 触摸监听重写_第六十四回:Android中UI控件之SeekBar

    各位看官们,大家好,上一回中咱们说的是Android中UI控件之ProgressBar的例子,这一回咱们的例子是UI控件之SeekBar.闲话休提,言归正转.让我们一起Talk Android吧! 看 ...

  7. android中倒计时控件CountDownTimer分析

    android中倒计时控件CountDownTimer分析1 示例代码 new CountDownTimer(10000, 1000) {public void onTick(long millisU ...

  8. android studio 画控件,Android Studio 基础控件使用

    TextView android:gravity="center" //文字对其方式 top bottom left right center android:textColor= ...

  9. listview控件Android,Android中ListView控件的简单使用

    文章引自郭霖<第一行代码> ListView允许用户通过手指上下滑动的方式将屏幕外的数据滚动到屏幕内,同时屏幕上原有的数据则会滚动出屏幕 使用LIstView控件 1 在布局文件中引入Li ...

  10. android 自定义switch控件,Android中switch自定义样式

    android 原生开关按钮控件 Switch 提供样式自定义方式,可供我们修改为适合我们开发使用的样式控件,自定义样式过程如下: 自定义switch切换drawable 新建swith_thumb. ...

最新文章

  1. 中国人工智能学会通讯——人工智能在各医学亚专科的发展现状及趋势 1.3 人工智能在各医学亚专科的发展态势...
  2. 前端构建工具gulp之基本介绍
  3. Install oracle 10g on RHEL 5.6--quick reference
  4. linux命令(8)wc
  5. 数字图像处理:视觉系统中的坐标系介绍
  6. maven自带clean_maven之clean、install命令
  7. RxHttp 一条链发送请求之注解处理器 Generated API(四)
  8. Tomcat环境部署以及tomcat多实例搭建(同一台机器)
  9. 深度学习面试题汇总大全(转)
  10. XML中输入特殊符号
  11. 公益/广告-bloggerads广告是为了钱?[非推广文章哈]
  12. Ubuntu出现“dpkg: 依赖关系问题使得libbsd0:i386的配置工作不能继续”错误
  13. QString 转toLatin1 toUtf8 toLocal8Bit区别
  14. js 移动端网页特效+移动端轮播图案例+移动端常用开发插件的使用
  15. Hash表_拉链法_开放寻址法_模拟散列表
  16. EasyPoi 导出表格并设置表头
  17. MCUXpresso开发NXP RT1060(3)——移植LVGL到NXP RT1060
  18. git上传文件到gitee
  19. 复制网页上不能复制的文章的方法
  20. 【LeetCode 1833】雪糕的最大数量

热门文章

  1. Word将英文数字全部修改为Times New Roman字体
  2. 截止失真放大电路_模拟放大器
  3. 遭遇应用程序正常初始化失败
  4. 象棋马走日步数计算流程图
  5. c 语言 输入一个英文字母,判断该字母是大写还是小写,c语言从键盘输入一个字符,判断该字符是大写英文字母.小写英文字母还是数字....
  6. 2022年小游戏----游戏背包系统之搭建背包UI
  7. c语言程序经过链接以后生成的文件名的后缀为,请多多指教,感激不尽11.C语言程序经过编译以后生成的文件名的后缀为( ).A..c B..obj C..exe D.....
  8. 最新!!2018南京买房政策大全
  9. 字节女实习生被通报批评,只因晚上12点就睡觉
  10. html背景为视频教程,HTML+CSS入门 如何将视频设置为网页背景