酷狗app高仿系列
2015-08-22 12:05
471 查看
好。。。从今天开始写博客。
下了下酷狗发现很不错啊,交互流畅,ui美观,那就从它开刀。先来个高仿篇。。。。
下面是首页,画了框的就是感兴趣的地方v
好咱们先分析下他的结构,它的侧滑更qq差不多,就是多了个耳朵view在动,
还有就是在右边(我擦。。。是个人都知道的区别)。那么分析如果没有那个耳朵view再动是不是很简单啊,
用脚趾一想自定viewgroup,重写LinearLayout,重写HrizontalScrollview。。。。
是不是有很多方案。但是多了一个耳朵view LinearLayout就不太合适了。
因为不好布局是吧,重写viewGroup又太麻烦,那咱还是拿简单的HrizontalScrollview来开刀吧。
但是HrizontalScrollview可选的子view又是个要思考下的问题,
因为他不但有FrameLayout的功能还有灵活布置位置的功能。**
第一次使用markdown,有点生,擦 就放这里吧
这里有好几个id是定义在res下的因为这样方便侧滑控件的重复使用,
这个layout的作用 起到一个框架的作用,搭了一个架子,
相当于现在做房子做了个水泥柱,没有砌砖。
哦。。差点忘记了,id定义在res还有个作用是那个耳朵view是在relativelayout的第一个位置的,
如果直接写@+id的话编译器是会报错的。
好,该我们的侧滑控件MenuHorizontalScrollView登场了。。。。
首先来分析下这侧滑控件要做的事情:
1.能够滑动,且能够到一定位置时滚动到指定的位置,即menu的open和close。
(废话,侧滑没有这个功能还叫侧滑吗。。。)
2.要解决垂直滚动和水平滚动的冲突
暂时先是这两个问题
好,开始我们的coding
是不是很简单啊。就那么几十行代码。侧滑已经很多了。相信大家很容易明白。
不然拦截没有效果,大家可以去看下HorizontalScrollView的源码。
至于onScrollChanged方法里面的动画变化处理,
因为是模仿不是正规的开发,所以就直接使用了属性动画,
要是要兼容的话可以使用开源库nineoldandroids.
来看下效果:
就两句代码句搞定了,改变下view的绘制顺序,好吧,这样我们的侧滑就完成了,哎。。。这天气是中暑的节奏。。先去喝杯水。
接下来呢是重头戏,上面的都是铺贴,房基是吧。
那么开始我们的内容区域吧。。。。
我们先来分析下哈
他不是两个activity而是在一个activity的一个容器里,是吧,
不然,下面的那个播放音乐的就不好搞了。
我们来思考下,这个怎么做好呢。。。
1.用一个FrameLayout作为容器,然后不断的添加view?
看来是个不错的选择,那么我们就先FrameLayout试试。
还是使用LayoutTransition呢?
3.滑动返回又怎么处理呢?直接自定义?还是v4包里的viewDragHelp?
进过试验和考虑我选择了使用FrameLayout作为容器
动画这一块使用预备容器的做法,因为“在addview是添加时自己加动画?
还是使用LayoutTransition呢”都不理想,在低端机下回闪一下体验不好。
滑动使用viewdraghelp可以减少很多代码量啊。
使用view作为activity一样的用,那么我们必须封装一下啊 是吧,
不然每次都一大堆重复代码,还能不能愉快的下班了。
再来一个基类
那首页view代码来分析下
看到没有 我们的mainactivity使用new MainPager(this, contentFl);
就可以把mainViewpager添加到容器中了,
有点犯困,这里写的有点乱。最后附上所有代码,估计看大代码就情绪了,
最后上我们最核心的类
代码里都有注释,最主要的是出现动画哪里,都加了一个辅助容器,
不然会出现闪一下的问题,自己可以试验下。
还加了一个缓存view,就是当view添加到多余三个时要把底部的view从容器移除,
不然侧滑菜单是会很卡顿,因为都看不到了,侧滑时还一直draw,也没有意思了,
是吧,所以就移除把,当要的时候再添加上去。界面的话没有去抠图,
程序员要的是核心代码和思想不是几个图片,是吧。
好了今天就到这里了。。。。困到不行啊,,眯会去。。。
哪里上传代码啊。。。晕
第一次写博客,哎,,第一次真是痛啊。后续持续更新,今天就先到这了
源代码
下了下酷狗发现很不错啊,交互流畅,ui美观,那就从它开刀。先来个高仿篇。。。。
下面是首页,画了框的就是感兴趣的地方v
好咱们先分析下他的结构,它的侧滑更qq差不多,就是多了个耳朵view在动,
还有就是在右边(我擦。。。是个人都知道的区别)。那么分析如果没有那个耳朵view再动是不是很简单啊,
用脚趾一想自定viewgroup,重写LinearLayout,重写HrizontalScrollview。。。。
是不是有很多方案。但是多了一个耳朵view LinearLayout就不太合适了。
因为不好布局是吧,重写viewGroup又太麻烦,那咱还是拿简单的HrizontalScrollview来开刀吧。
但是HrizontalScrollview可选的子view又是个要思考下的问题,
因为他不但有FrameLayout的功能还有灵活布置位置的功能。**
第一次使用markdown,有点生,擦 就放这里吧
<!--这是侧滑的horizontalScrollview--> <com.example.kugou.view.MenuHorizontalScrollView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/menu_horizontal" android:layout_width="match_parent" android:layout_height="match_parent" android:fillViewport="true" tools:context="com.example.kugou.MainActivity" > <!--这是最外层的relativelayout ,为什么要自定义呢,博客等下会说--> <com.example.kugou.view.MenuRelativeLayout android:layout_width="match_parent" android:layout_height="match_parent" android:background="#55ff0000" > <!--会动的那个耳朵view--> <ImageView android:id="@id/main_menu_icon_id" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerVertical="true" android:layout_toRightOf="@id/main_content_id" android:src="@drawable/ic_launcher" /> <!--内容区域--> <RelativeLayout android:id="@id/main_content_id" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#ccc" > </RelativeLayout> <!--菜单区域--> <LinearLayout android:id="@id/main_menu_id" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_toRightOf="@id/main_content_id" android:orientation="vertical" > </LinearLayout> </com.example.kugou.view.MenuRelativeLayout> </com.example.kugou.view.MenuHorizontalScrollView>
这里有好几个id是定义在res下的因为这样方便侧滑控件的重复使用,
这个layout的作用 起到一个框架的作用,搭了一个架子,
相当于现在做房子做了个水泥柱,没有砌砖。
哦。。差点忘记了,id定义在res还有个作用是那个耳朵view是在relativelayout的第一个位置的,
如果直接写@+id的话编译器是会报错的。
好,该我们的侧滑控件MenuHorizontalScrollView登场了。。。。
首先来分析下这侧滑控件要做的事情:
1.能够滑动,且能够到一定位置时滚动到指定的位置,即menu的open和close。
(废话,侧滑没有这个功能还叫侧滑吗。。。)
2.要解决垂直滚动和水平滚动的冲突
暂时先是这两个问题
好,开始我们的coding
public class MenuHorizontalScrollView extends HorizontalScrollView { /*这里设置menu为屏幕宽4/5*/ private final float menuScale = 4 / 5f; private final int INVALID_POINTER = -1; private int screenWidth; private int menuViewWidth; private int iconViewWidth; private boolean isOpen; /*拿到contentChild和menuChild 好做一些动画*/ private ViewGroup contentChild; private ViewGroup menuChild; private View iconView; /*解决滑动冲突东东*/ private int mActivePointerId; private float mInitialMotionX; private float mInitialMotionY; public MenuHorizontalScrollView(Context context) { this(context, null); // TODO Auto-generated constructor stub } public MenuHorizontalScrollView(Context context, AttributeSet attrs) { this(context, attrs, 0); // TODO Auto-generated constructor stub } public MenuHorizontalScrollView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); // TODO Auto-generated constructor stub initView(context); } private void initView(Context context) { // TODO Auto-generated method stub screenWidth = context.getResources().getDisplayMetrics().widthPixels; menuViewWidth = (int) (screenWidth * menuScale); } /** * 因为我们的menu是固定大小的 是吧 所以我们要给他设置下 * 再测量之前设置下layoutParams是可以直接起作用的 * 因为在测量的时候来拿出layoutParams * 相信玩过自定义view都都看到过 是吧 * * @param widthMeasureSpec * @param heightMeasureSpec */ @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // TODO Auto-generated method stub ViewGroup rlChildview = (ViewGroup) getChildAt(0); rlChildview.getLayoutParams().width = screenWidth + menuViewWidth; contentChild = (ViewGroup) rlChildview .findViewById(R.id.main_content_id); contentChild.getLayoutParams().width = screenWidth; menuChild = (ViewGroup) rlChildview.findViewById(R.id.main_menu_id); menuChild.getLayoutParams().width = menuViewWidth; super.onMeasure(widthMeasureSpec, heightMeasureSpec); contentChild.setPivotX(screenWidth); contentChild.setPivotY(contentChild.getMeasuredHeight() / 2); iconViewWidth = iconView.getMeasuredWidth(); } /*这个方法就是在xmlParse后调用的 可以找到我们的一些控件*/ @Override protected void onFinishInflate() { // TODO Auto-generated method stub super.onFinishInflate(); iconView = findViewById(R.id.main_menu_icon_id); } /* 解决垂直和水平滑动的问题*/ @Override public boolean onInterceptTouchEvent(MotionEvent ev) { // TODO Auto-generated method stub int action = ev.getAction() & MotionEventCompat.ACTION_MASK; switch (action) { case MotionEvent.ACTION_DOWN: int index = MotionEventCompat.getActionIndex(ev); mActivePointerId = MotionEventCompat.getPointerId(ev, index); if (mActivePointerId == INVALID_POINTER) { break; } mInitialMotionX = MotionEventCompat.getX(ev, index); mInitialMotionY = MotionEventCompat.getY(ev, index); break; case MotionEvent.ACTION_MOVE: int lastIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId); final float lastX = MotionEventCompat.getX(ev, lastIndex); final float lastY = MotionEventCompat.getY(ev, lastIndex); final float destX = Math.abs(lastX - mInitialMotionX); final float destY = Math.abs(lastY - mInitialMotionY); if (destX > destY) { if (!isOpen && lastX - mInitialMotionX > 0) { return false; } /*这里一定不能直接返回true 因为在父类要做一些判断和初始化变量不然没有效果了*/ return super.onInterceptTouchEvent(ev); } else { return false; } case MotionEvent.ACTION_UP: break; } return super.onInterceptTouchEvent(ev); } /** * 因为本身就是horizontalScrollview 就代用滚动 * 所以我们只需要判断一下actionup 是否要打开还是关闭menu * @param ev * @return */ @Override public boolean onTouchEvent(MotionEvent ev) { // TODO Auto-generated method stub if (ev.getAction() == MotionEvent.ACTION_UP) { if (getScrollX() > menuViewWidth / 2) { this.smoothScrollTo(menuViewWidth, 0); isOpen = true; } else { this.smoothScrollTo(0, 0); isOpen = false; } return true; } return super.onTouchEvent(ev); } /** * 这里呢就是做一些动画了 */ @Override protected void onScrollChanged(int l, int t, int oldl, int oldt) { // TODO Auto-generated method stub super.onScrollChanged(l, t, oldl, oldt); float scale = l * 1.0f / menuViewWidth; float menuScale = 0.8f + 0.2f * scale; float contentScale = 1.0f - 0.2f * scale; /* menu change */ PropertyValuesHolder menuScaleX = PropertyValuesHolder.ofFloat( "scaleX", menuScale); PropertyValuesHolder menuScaleY = PropertyValuesHolder.ofFloat( "scaleY", menuScale); PropertyValuesHolder menuTranslateY = PropertyValuesHolder.ofFloat( "translationX", -(1 - scale) * menuViewWidth * 0.2f); ObjectAnimator menuOA = ObjectAnimator.ofPropertyValuesHolder( menuChild, menuScaleX, menuScaleY, menuTranslateY); menuOA.setDuration(0); menuOA.start(); /* content change */ PropertyValuesHolder contentScaleX = PropertyValuesHolder.ofFloat( "scaleX", contentScale); PropertyValuesHolder contentScaleY = PropertyValuesHolder.ofFloat( "scaleY", contentScale); ObjectAnimator contentOA = ObjectAnimator.ofPropertyValuesHolder( contentChild, contentScaleX, contentScaleY); contentOA.setDuration(0); contentOA.start(); /* menu icon change */ ObjectAnimator iconOA = ObjectAnimator.ofFloat(iconView, "translationX", -scale * iconViewWidth * 0.5f); iconOA.setDuration(0); iconOA.start(); } /*暂时给哪儿耳朵View使用,控制菜单的显示和隐藏*/ public void toggleMenu() { if (isOpen) { this.smoothScrollTo(0, 0); } else { this.smoothScrollTo(menuViewWidth, 0); } isOpen = !isOpen; } }
是不是很简单啊。就那么几十行代码。侧滑已经很多了。相信大家很容易明白。
不然拦截没有效果,大家可以去看下HorizontalScrollView的源码。
至于onScrollChanged方法里面的动画变化处理,
因为是模仿不是正规的开发,所以就直接使用了属性动画,
要是要兼容的话可以使用开源库nineoldandroids.
来看下效果:
是不是比较坑,完全不是我们想要的啊。该上的没上,该下的没下(到底是谁上谁下呢,,,,,我去 跑腿了。。。) 我们来分析下原因。返回到上面我们的布局文件,你会发现什么,用火眼看下,是不是不坑,是我们的代码写的坑是吧。前人挖坑后人填坑,前人种树后人乘凉,我们希望我们新进的公司的前辈都是种树的不是挖坑的,我们好乘凉喝茶看妹子,打豆豆啊,不然天天填坑,就不能愉快的下班找妹子约会了。。。嗨嗨。。中风了 所以我们要自定义RelativeLayout 是吧,前面已经说了。好开始我们的改造view
public MenuRelativeLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); // TODO Auto-generated constructor stub setChildrenDrawingOrderEnabled(true); } @Override protected int getChildDrawingOrder(int childCount, int i) { // TODO Auto-generated method stub return childCount - i - 1; }
就两句代码句搞定了,改变下view的绘制顺序,好吧,这样我们的侧滑就完成了,哎。。。这天气是中暑的节奏。。先去喝杯水。
接下来呢是重头戏,上面的都是铺贴,房基是吧。
那么开始我们的内容区域吧。。。。
我们先来分析下哈
他不是两个activity而是在一个activity的一个容器里,是吧,
不然,下面的那个播放音乐的就不好搞了。
我们来思考下,这个怎么做好呢。。。
1.用一个FrameLayout作为容器,然后不断的添加view?
看来是个不错的选择,那么我们就先FrameLayout试试。
还是使用LayoutTransition呢?
3.滑动返回又怎么处理呢?直接自定义?还是v4包里的viewDragHelp?
进过试验和考虑我选择了使用FrameLayout作为容器
动画这一块使用预备容器的做法,因为“在addview是添加时自己加动画?
还是使用LayoutTransition呢”都不理想,在低端机下回闪一下体验不好。
滑动使用viewdraghelp可以减少很多代码量啊。
使用view作为activity一样的用,那么我们必须封装一下啊 是吧,
不然每次都一大堆重复代码,还能不能愉快的下班了。
定义一个接口,
public interface IViewPager { View attachView(MenuContentFrameLayout rootView); }
再来一个基类
public abstract class BaseViewPager implements IViewPager { protected View contentView; protected Context mContext; protected MenuContentFrameLayout rootView; public BaseViewPager(Context mContext, MenuContentFrameLayout rootView) { // TODO Auto-generated constructor stub this.mContext = mContext; contentView = attachView(rootView); this.rootView = rootView; initView(); initValue(); setListener(); } protected abstract void initView(); protected abstract void initValue(); protected abstract void setListener(); }
那首页view代码来分析下
public class MainPager extends BaseViewPager { private Button goBtn; public MainPager(Context context, MenuContentFrameLayout rootView) { super(context, rootView); // TODO Auto-generated constructor stub } @Override public View attachView(MenuContentFrameLayout rootView) { // TODO Auto-generated method stub return LayoutInflater.from(mContext).inflate( R.layout.main_content_layout, rootView, true); } @Override protected void initView() { // TODO Auto-generated method stub goBtn = (Button) contentView.findViewById(R.id.btn_go); } @Override protected void initValue() { // TODO Auto-generated method stub } @Override protected void setListener() { // TODO Auto-generated method stub goBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub new FriendListPager(mContext, rootView); } }); } }这里写代码片
public class MainActivity extends Activity { private MenuHorizontalScrollView menuHroizontalScrollView; private ImageView menuIconView; private MenuContentFrameLayout contentFl; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); initValue(); initListener(); } private void initView() { // TODO Auto-generated method stub menuHroizontalScrollView = (MenuHorizontalScrollView) findViewById(R.id.menu_horizontal); menuIconView = (ImageView) findViewById(R.id.main_menu_icon_id); contentFl = (MenuContentFrameLayout) findViewById(R.id.content_fl); } private void initValue() { // TODO Auto-generated method stub new MainPager(this, contentFl); } private void initListener() { // TODO Auto-generated method stub menuIconView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub menuHroizontalScrollView.toggleMenu(); } }); } }
看到没有 我们的mainactivity使用new MainPager(this, contentFl);
就可以把mainViewpager添加到容器中了,
有点犯困,这里写的有点乱。最后附上所有代码,估计看大代码就情绪了,
最后上我们最核心的类
public class MenuContentFrameLayout extends FrameLayout { /*重点滑动view*/ private ViewDragHelper mDragHelper; private static final int MIN_FLING_VELOCITY = 400; private int mActivePointerId; private final int INVALID_POINTER = -1; private float mInitialMotionX; private float mInitialMotionY; private DisplayMetrics displayMetrics; /*保存添加的view*/ private ArrayList<View> stackViewList; /*回收的view*/ private ArrayList<View> recycleViewList; /*做动画的辅助view*/ private ContentSubFrameLayout subContentLayout; public MenuContentFrameLayout(Context context) { this(context, null); // TODO Auto-generated constructor stub } public MenuContentFrameLayout(Context context, AttributeSet attrs) { this(context, attrs, 0); // TODO Auto-generated constructor stub } public MenuContentFrameLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); // TODO Auto-generated constructor stub initView(context); } private void initView(Context context) { // TODO Auto-generated method stub mDragHelper = ViewDragHelper.create(this, 1.0f, new MyCallback()); displayMetrics = context.getResources().getDisplayMetrics(); final float minVel = MIN_FLING_VELOCITY * displayMetrics.density + 0.5f; mDragHelper.setMinVelocity(minVel); stackViewList = new ArrayList<View>(); recycleViewList = new ArrayList<View>(); } /** * 这里处理滑动返回和滑动冲突处理 * @param ev * @return */ @Override public boolean onInterceptTouchEvent(MotionEvent ev) { // TODO Auto-generated method stub int action = ev.getAction() & MotionEventCompat.ACTION_MASK; switch (action) { case MotionEvent.ACTION_DOWN: int index = MotionEventCompat.getActionIndex(ev); mActivePointerId = MotionEventCompat.getPointerId(ev, index); if (mActivePointerId == INVALID_POINTER) { break; } mInitialMotionX = MotionEventCompat.getX(ev, index); mInitialMotionY = MotionEventCompat.getY(ev, index); break; case MotionEvent.ACTION_MOVE: int lastIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId); final float lastX = MotionEventCompat.getX(ev, lastIndex); final float lastY = MotionEventCompat.getY(ev, lastIndex); final float destX = Math.abs(lastX - mInitialMotionX); final float destY = Math.abs(lastY - mInitialMotionY); if (destX > destY) { return mDragHelper.shouldInterceptTouchEvent(ev); } else { return false; } case MotionEvent.ACTION_UP: break; } return mDragHelper.shouldInterceptTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent event) { // TODO Auto-generated method stub mDragHelper.processTouchEvent(event); return true; } @Override public void computeScroll() { // TODO Auto-generated method stub if (mDragHelper.continueSettling(true)) { invalidate(); } } /** * v4包里的滑动回调 */ private class MyCallback extends ViewDragHelper.Callback { /*是否要捕获*/ @Override public boolean tryCaptureView(View arg0, int arg1) { // TODO Auto-generated method stub return true; } /*水平滑动距离*/ @Override public int clampViewPositionHorizontal(View child, int left, int dx) { // TODO Auto-generated method stub if (getChildCount() == 1) left -= dx * 2 / 3; if (left < 0) left = 0; return left; } /*当手机松开时*/ @Override public void onViewReleased(View releasedChild, float xvel, float yvel) { // TODO Auto-generated method stub if (getChildCount() == 1) { mDragHelper.settleCapturedViewAt(0, releasedChild.getTop()); } else { final float offset = releasedChild.getLeft() * 1.0f / releasedChild.getWidth(); mDragHelper.settleCapturedViewAt(xvel > 0 || xvel == 0 && offset >= 0.5 ? releasedChild.getWidth() : 0, releasedChild.getTop()); } invalidate(); } /*位置变化时,这里可以做动画*/ @Override public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) { // TODO Auto-generated method stub if (getChildCount() > 1) { if (left >= changedView.getWidth()) { removeView(changedView); stackViewList.remove(stackViewList.size() - 1); if (recycleViewList.size() > 0) { addView(recycleViewList.get(recycleViewList.size() - 1), 0); recycleViewList.remove(recycleViewList.size() - 1); } } } } @Override public int getViewHorizontalDragRange(View child) { // TODO Auto-generated method stub return child.getWidth(); } } @Override public void addView(View child, android.view.ViewGroup.LayoutParams params) { // TODO Auto-generated method stub if (child instanceof ContentSubFrameLayout) { super.addView(child, params); } else { if (stackViewList.size() == 0) { super.addView(child, params); } else { if (stackViewList.size() >= 2) { View oneChildView = getChildAt(0); recycleViewList.add(oneChildView); removeView(oneChildView); } /*添加一个辅助容器来作出现动画,避免闪一下问题*/ final ContentSubFrameLayout subContentLayout = new ContentSubFrameLayout( getContext()); final FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams( FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT); layoutParams.setMargins(displayMetrics.widthPixels, 0, 0, 0); addView(subContentLayout, layoutParams); subContentLayout.addView(child, params); /*处理动画*/ ObjectAnimator subOA = ObjectAnimator.ofInt(subContentLayout, "left", displayMetrics.widthPixels, 0); subOA.setDuration(600); subOA.start(); subOA.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { // TODO Auto-generated method stub final FrameLayout.LayoutParams layoutParams = (LayoutParams) subContentLayout .getLayoutParams(); layoutParams.leftMargin = 0; } }); } stackViewList.add(child); } } }
代码里都有注释,最主要的是出现动画哪里,都加了一个辅助容器,
不然会出现闪一下的问题,自己可以试验下。
还加了一个缓存view,就是当view添加到多余三个时要把底部的view从容器移除,
不然侧滑菜单是会很卡顿,因为都看不到了,侧滑时还一直draw,也没有意思了,
是吧,所以就移除把,当要的时候再添加上去。界面的话没有去抠图,
程序员要的是核心代码和思想不是几个图片,是吧。
好了今天就到这里了。。。。困到不行啊,,眯会去。。。
哪里上传代码啊。。。晕
第一次写博客,哎,,第一次真是痛啊。后续持续更新,今天就先到这了
源代码
相关文章推荐
- Android:Notification的生成与取消
- Android 自定义ActionBar
- Android之在Dialog中添加菜单【自定义Dialog】
- opencv中的approxPolyDP函数和boundingRect函数说明
- 判断客户端是iPad、安卓还是ios
- 自定义view,仿淘宝快递的物流信息的时间轴
- Android之Toast入门版【常用方法汇总】
- cocos2d-x简单绘图(DrawNode)
- JS通过Object创建简单对象
- Android程序设计:ExpandableListView或ListView的Item的子事件写法
- Android中attr自定义属性,记录而已
- Android之在Dialog中添加单选按钮【自定义Dialog】
- Android之自定义Toast
- iOS单例模式
- cocos2d-js 2048带动画实现
- 初入android驱动开发之字符设备(四-中断)
- (二)、Android ListView滑动过程中图片显示重复错位闪烁问题解决
- android.os.NetworkOnMainThreadException异常
- Android中Activity四种启动模式和taskAffinity属性详解
- Android自定义控件实战——水流波动效果的实现WaveView