Android下拉刷新上拉加载控件,对所有View通用!

转载[http://blog.csdn.net/zhongkejingwang/article/details/38868463](http://blog.csdn.net/zhongkejingwang/article/details/38868463) 前面写过一篇关于下拉刷新控件的博客[下拉刷新控件终结者:PullToRefreshLayout](http://blog.csdn.net/zhongkejingwang/article/details/38340701),后来看到好多人还有上拉加载更多的需求,于是就在前面下拉刷新控件的基础上进行了改进,加了上拉加载的功能。不仅如此,我已经把它改成了对所有View都通用!可以随心所欲使用这两个功能~~ 我做了一个大集合的demo,实现了ListView、GridView、ExpandableListView、ScrollView、WebView、ImageView、TextView的下拉刷新和上拉加载。后面会提供demo的下载地址。(csdn上的demo有小bug,最新代码已上传到github:[https://github.com/jingchenUSTC/PullToRefreshAndLoad](https://github.com/jingchenUSTC/PullToRefreshAndLoad)) 依照惯例,下面将会是一大波效果图: demo首页也是可下拉的ListView,在底下可以加入table: ![](http://img.blog.csdn.net/20140827132732390?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvemhvbmdrZWppbmd3YW5n/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) ListView: ![](http://img.blog.csdn.net/20140827133113883?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvemhvbmdrZWppbmd3YW5n/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) GridView: ![](http://img.blog.csdn.net/20140827133157922?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvemhvbmdrZWppbmd3YW5n/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) ExpandableListView: ![](http://img.blog.csdn.net/20140827133245580?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvemhvbmdrZWppbmd3YW5n/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) ScrollView: ![](http://img.blog.csdn.net/20140827134146109?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvemhvbmdrZWppbmd3YW5n/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) WebView: ![](http://img.blog.csdn.net/20140827134443946?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvemhvbmdrZWppbmd3YW5n/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) ImageView: ![](http://img.blog.csdn.net/20140827135142900?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvemhvbmdrZWppbmd3YW5n/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) TextView: ![](http://img.blog.csdn.net/20140827135212228?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvemhvbmdrZWppbmd3YW5n/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) 很不错吧?最后的ImageView和TextView是最简单的,直接在下面的接口方法里返回true。 增加上拉加载很简单,和管理下拉头一样,再多管理一个上拉头,也不费事;至于把它改成通用的就需要统一一下View的行为了,为此,我定义了这样一个接口: <div class="dp-highlighter bg_java"> <div class="bar"> <div class="tools"> **[java]** [view plain](http://blog.csdn.net/zhongkejingwang/article/details/38868463#)<span class="tracking-ad" data-mod="popu_168"><span class="tracking-ad" data-mod="popu_168"> [copy](http://blog.csdn.net/zhongkejingwang/article/details/38868463#)</span></span> <div> <embed id="ZeroClipboardMovie_1" src="http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf" type="application/x-shockwave-flash" width="18" height="18" align="middle" name="ZeroClipboardMovie_1"> </embed> </div> </div> </div> - <span class="keyword">package</span> com.jingchen.pulltorefresh.pullableview; - - <span class="keyword">public</span> <span class="keyword">interface</span> Pullable - { - <span class="comment">/**</span> - <span class="comment"> * 判断是否可以下拉,如果不需要下拉功能可以直接return false</span> - <span class="comment"> * </span> - <span class="comment"> * @return true如果可以下拉否则返回false</span> - <span class="comment"> */</span> - <span class="keyword">boolean</span> canPullDown(); - - <span class="comment">/**</span> - <span class="comment"> * 判断是否可以上拉,如果不需要上拉功能可以直接return false</span> - <span class="comment"> * </span> - <span class="comment"> * @return true如果可以上拉否则返回false</span> - <span class="comment"> */</span> - <span class="keyword">boolean</span> canPullUp(); - } </div> 从接口名就可以看出它是一个提供判断是否可拉的方法的接口。这个接口的两个方法,canPullDown()是判断何时可以下拉的方法,canPullUp()则是判断何时可以上拉,我在demo中的判断是滑到顶部的时候可以下拉,滑到底部的时候可以上拉。所有需要上拉和下拉的View都需要实现这个接口。后面会给出一些View的实现。先来看看改进后的自定义的布局PullToRefreshLayout,增加了一个上拉头,下拉头和上拉头之间的View是实现了Pullable接口的pullableView。相比前面的版本,这里有改动的需要注意的地方如下:1、增加了上拉头,相应的也增加了控制变量。 2、拉动时消除content_view事件防止误触发不再使用反射,直接设置 event.setAction(MotionEvent.ACTION_CANCEL)。 3、消除了拉动过程中的多点触碰导致的剧变。 4、不再设置content_view的onTouListener,让使用者可以更加自由的设置监听器。 这个PullToRefreshLayout只负责管理三个控件,如果一个View需要有上拉下拉功能则只需实现接口就行了。下面看PullToRefreshLayout的代码,注释写了好多: <div class="dp-highlighter bg_java"> <div class="bar"> <div class="tools"> **[java]** [view plain](http://blog.csdn.net/zhongkejingwang/article/details/38868463#)<span class="tracking-ad" data-mod="popu_168"><span class="tracking-ad" data-mod="popu_168"> [copy](http://blog.csdn.net/zhongkejingwang/article/details/38868463#)</span></span> <div> <embed id="ZeroClipboardMovie_2" src="http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf" type="application/x-shockwave-flash" width="18" height="18" align="middle" name="ZeroClipboardMovie_2"> </embed> </div> </div> </div> - <span class="keyword">package</span> com.jingchen.pulltorefresh; - - <span class="keyword">import</span> java.util.Timer; - <span class="keyword">import</span> java.util.TimerTask; - - <span class="keyword">import</span> <a href=<span class="string">&#8220;http://lib.csdn.net/base/15&#8221;</span> <span class="keyword">class</span>=<span class="string">&#8220;replace_word&#8221;</span> title=<span class="string">&#8220;undefined&#8221;</span> target=<span class="string">&#8220;_blank&#8221;</span> style=<span class="string">&#8220;color:#df3434; font-weight:bold;&#8221;</span>>android</a>.content.Context; - <span class="keyword">import</span> android.graphics.Canvas; - <span class="keyword">import</span> android.graphics.LinearGradient; - <span class="keyword">import</span> android.graphics.Paint; - <span class="keyword">import</span> android.graphics.Paint.Style; - <span class="keyword">import</span> android.graphics.RectF; - <span class="keyword">import</span> android.graphics.Shader.TileMode; - <span class="keyword">import</span> android.os.Handler; - <span class="keyword">import</span> android.os.Message; - <span class="keyword">import</span> android.util.AttributeSet; - <span class="keyword">import</span> android.util.Log; - <span class="keyword">import</span> android.view.MotionEvent; - <span class="keyword">import</span> android.view.View; - <span class="keyword">import</span> android.view.ViewGroup; - <span class="keyword">import</span> android.view.animation.AnimationUtils; - <span class="keyword">import</span> android.view.animation.LinearInterpolator; - <span class="keyword">import</span> android.view.animation.RotateAnimation; - <span class="keyword">import</span> android.widget.RelativeLayout; - <span class="keyword">import</span> android.widget.TextView; - - <span class="keyword">import</span> com.jingchen.pulltorefresh.pullableview.Pullable; - - <span class="comment">/**</span> - <span class="comment"> * 自定义的布局,用来管理三个子控件,其中一个是下拉头,一个是包含内容的pullableView(可以是实现Pullable接口的的任何View),</span> - <span class="comment"> * 还有一个上拉头</span> - <span class="comment"> * </span> - <span class="comment"> * @author 陈靖</span> - <span class="comment"> */</span> - <span class="keyword">public</span> <span class="keyword">class</span> PullToRefreshLayout <span class="keyword">extends</span> RelativeLayout - { - <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> String TAG = <span class="string">&#8220;PullToRefreshLayout&#8221;</span>; - <span class="comment">// 初始状态</span> - <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> INIT = <span class="number"></span>; - <span class="comment">// 释放刷新</span> - <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> RELEASE_TO_REFRESH = <span class="number">1</span>; - <span class="comment">// 正在刷新</span> - <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> REFRESHING = <span class="number">2</span>; - <span class="comment">// 释放加载</span> - <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> RELEASE_TO_LOAD = <span class="number">3</span>; - <span class="comment">// 正在加载</span> - <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> LOADING = <span class="number">4</span>; - <span class="comment">// 操作完毕</span> - <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> DONE = <span class="number">5</span>; - <span class="comment">// 当前状态</span> - <span class="keyword">private</span> <span class="keyword">int</span> state = INIT; - <span class="comment">// 刷新回调接口</span> - <span class="keyword">private</span> OnRefreshListener mListener; - <span class="comment">// 刷新成功</span> - <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> SUCCEED = <span class="number"></span>; - <span class="comment">// 刷新失败</span> - <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> FAIL = <span class="number">1</span>; - <span class="comment">// 按下Y坐标,上一个事件点Y坐标</span> - <span class="keyword">private</span> <span class="keyword">float</span> downY, lastY; - - <span class="comment">// 下拉的距离。注意:pullDownY和pullUpY不可能同时不为0</span> - <span class="keyword">public</span> <span class="keyword">float</span> pullDownY = <span class="number"></span>; - <span class="comment">// 上拉的距离</span> - <span class="keyword">private</span> <span class="keyword">float</span> pullUpY = <span class="number"></span>; - - <span class="comment">// 释放刷新的距离</span> - <span class="keyword">private</span> <span class="keyword">float</span> refreshDist = <span class="number">200</span>; - <span class="comment">// 释放加载的距离</span> - <span class="keyword">private</span> <span class="keyword">float</span> loadmoreDist = <span class="number">200</span>; - - <span class="keyword">private</span> MyTimer timer; - <span class="comment">// 回滚速度</span> - <span class="keyword">public</span> <span class="keyword">float</span> MOVE_SPEED = <span class="number">8</span>; - <span class="comment">// 第一次执行布局</span> - <span class="keyword">private</span> <span class="keyword">boolean</span> isLayout = <span class="keyword">false</span>; - <span class="comment">// 在刷新过程中滑动操作</span> - <span class="keyword">private</span> <span class="keyword">boolean</span> isTouch = <span class="keyword">false</span>; - <span class="comment">// 手指滑动距离与下拉头的滑动距离比,中间会随正切函数变化</span> - <span class="keyword">private</span> <span class="keyword">float</span> radio = <span class="number">2</span>; - - <span class="comment">// 下拉箭头的转180°动画</span> - <span class="keyword">private</span> RotateAnimation rotateAnimation; - <span class="comment">// 均匀旋转动画</span> - <span class="keyword">private</span> RotateAnimation refreshingAnimation; - - <span class="comment">// 下拉头</span> - <span class="keyword">private</span> View refreshView; - <span class="comment">// 下拉的箭头</span> - <span class="keyword">private</span> View pullView; - <span class="comment">// 正在刷新的图标</span> - <span class="keyword">private</span> View refreshingView; - <span class="comment">// 刷新结果图标</span> - <span class="keyword">private</span> View refreshStateImageView; - <span class="comment">// 刷新结果:成功或失败</span> - <span class="keyword">private</span> TextView refreshStateTextView; - - <span class="comment">// 上拉头</span> - <span class="keyword">private</span> View loadmoreView; - <span class="comment">// 上拉的箭头</span> - <span class="keyword">private</span> View pullUpView; - <span class="comment">// 正在加载的图标</span> - <span class="keyword">private</span> View loadingView; - <span class="comment">// 加载结果图标</span> - <span class="keyword">private</span> View loadStateImageView; - <span class="comment">// 加载结果:成功或失败</span> - <span class="keyword">private</span> TextView loadStateTextView; - - <span class="comment">// 实现了Pullable接口的View</span> - <span class="keyword">private</span> View pullableView; - <span class="comment">// 过滤多点触碰</span> - <span class="keyword">private</span> <span class="keyword">int</span> mEvents; - <span class="comment">// 这两个变量用来控制pull的方向,如果不加控制,当情况满足可上拉又可下拉时没法下拉</span> - <span class="keyword">private</span> <span class="keyword">boolean</span> canPullDown = <span class="keyword">true</span>; - <span class="keyword">private</span> <span class="keyword">boolean</span> canPullUp = <span class="keyword">true</span>; - - <span class="comment">/**</span> - <span class="comment"> * 执行自动回滚的handler</span> - <span class="comment"> */</span> - Handler updateHandler = <span class="keyword">new</span> Handler() - { - - <span class="annotation">@Override</span> - <span class="keyword">public</span> <span class="keyword">void</span> handleMessage(Message msg) - { - <span class="comment">// 回弹速度随下拉距离moveDeltaY增大而增大</span> - MOVE_SPEED = (<span class="keyword">float</span>) (<span class="number">8</span> + <span class="number">5</span> * Math.tan(Math.PI / <span class="number">2</span> - / getMeasuredHeight() * (pullDownY + Math.abs(pullUpY)))); - <span class="keyword">if</span> (!isTouch) - { - <span class="comment">// 正在刷新,且没有往上推的话则悬停,显示&#8221;正在刷新&#8230;&#8221;</span> - <span class="keyword">if</span> (state == REFRESHING && pullDownY <= refreshDist) - { - pullDownY = refreshDist; - timer.cancel(); - } <span class="keyword">else</span> <span class="keyword">if</span> (state == LOADING && -pullUpY <= loadmoreDist) - { - pullUpY = -loadmoreDist; - timer.cancel(); - } - - } - <span class="keyword">if</span> (pullDownY > <span class="number"></span>) - pullDownY -= MOVE_SPEED; - <span class="keyword">else</span> <span class="keyword">if</span> (pullUpY < <span class="number"></span>) - pullUpY += MOVE_SPEED; - <span class="keyword">if</span> (pullDownY < <span class="number"></span>) - { - <span class="comment">// 已完成回弹</span> - pullDownY = <span class="number"></span>; - pullView.clearAnimation(); - <span class="comment">// 隐藏下拉头时有可能还在刷新,只有当前状态不是正在刷新时才改变状态</span> - <span class="keyword">if</span> (state != REFRESHING && state != LOADING) - changeState(INIT); - timer.cancel(); - } - <span class="keyword">if</span> (pullUpY > <span class="number"></span>) - { - <span class="comment">// 已完成回弹</span> - pullUpY = <span class="number"></span>; - pullUpView.clearAnimation(); - <span class="comment">// 隐藏下拉头时有可能还在刷新,只有当前状态不是正在刷新时才改变状态</span> - <span class="keyword">if</span> (state != REFRESHING && state != LOADING) - changeState(INIT); - timer.cancel(); - } - <span class="comment">// 刷新布局,会自动调用onLayout</span> - requestLayout(); - } - - }; - - <span class="keyword">public</span> <span class="keyword">void</span> setOnRefreshListener(OnRefreshListener listener) - { - mListener = listener; - } - - <span class="keyword">public</span> PullToRefreshLayout(Context context) - { - <span class="keyword">super</span>(context); - initView(context); - } - - <span class="keyword">public</span> PullToRefreshLayout(Context context, AttributeSet attrs) - { - <span class="keyword">super</span>(context, attrs); - initView(context); - } - - <span class="keyword">public</span> PullToRefreshLayout(Context context, AttributeSet attrs, <span class="keyword">int</span> defStyle) - { - <span class="keyword">super</span>(context, attrs, defStyle); - initView(context); - } - - <span class="keyword">private</span> <span class="keyword">void</span> initView(Context context) - { - timer = <span class="keyword">new</span> MyTimer(updateHandler); - rotateAnimation = (RotateAnimation) AnimationUtils.loadAnimation( - context, R.anim.reverse_anim); - refreshingAnimation = (RotateAnimation) AnimationUtils.loadAnimation( - context, R.anim.rotating); - <span class="comment">// 添加匀速转动动画</span> - LinearInterpolator lir = <span class="keyword">new</span> LinearInterpolator(); - rotateAnimation.setInterpolator(lir); - refreshingAnimation.setInterpolator(lir); - } - - <span class="keyword">private</span> <span class="keyword">void</span> hide() - { - timer.schedule(<span class="number">5</span>); - } - - <span class="comment">/**</span> - <span class="comment"> * 完成刷新操作,显示刷新结果。注意:刷新完成后一定要调用这个方法</span> - <span class="comment"> */</span> - <span class="comment">/**</span> - <span class="comment"> * @param refreshResult</span> - <span class="comment"> * PullToRefreshLayout.SUCCEED代表成功,PullToRefreshLayout.FAIL代表失败</span> - <span class="comment"> */</span> - <span class="keyword">public</span> <span class="keyword">void</span> refreshFinish(<span class="keyword">int</span> refreshResult) - { - refreshingView.clearAnimation(); - refreshingView.setVisibility(View.GONE); - <span class="keyword">switch</span> (refreshResult) - { - <span class="keyword">case</span> SUCCEED: - <span class="comment">// 刷新成功</span> - refreshStateImageView.setVisibility(View.VISIBLE); - refreshStateTextView.setText(R.string.refresh_succeed); - refreshStateImageView - .setBackgroundResource(R.drawable.refresh_succeed); - <span class="keyword">break</span>; - <span class="keyword">case</span> FAIL: - <span class="keyword">default</span>: - <span class="comment">// 刷新失败</span> - refreshStateImageView.setVisibility(View.VISIBLE); - refreshStateTextView.setText(R.string.refresh_fail); - refreshStateImageView - .setBackgroundResource(R.drawable.refresh_failed); - <span class="keyword">break</span>; - } - <span class="comment">// 刷新结果停留1秒</span> - <span class="keyword">new</span> Handler() - { - <span class="annotation">@Override</span> - <span class="keyword">public</span> <span class="keyword">void</span> handleMessage(Message msg) - { - changeState(DONE); - hide(); - } - }.sendEmptyMessageDelayed(<span class="number"></span>, <span class="number">1000</span>); - } - - <span class="comment">/**</span> - <span class="comment"> * 加载完毕,显示加载结果。注意:加载完成后一定要调用这个方法</span> - <span class="comment"> * </span> - <span class="comment"> * @param refreshResult</span> - <span class="comment"> * PullToRefreshLayout.SUCCEED代表成功,PullToRefreshLayout.FAIL代表失败</span> - <span class="comment"> */</span> - <span class="keyword">public</span> <span class="keyword">void</span> loadmoreFinish(<span class="keyword">int</span> refreshResult) - { - loadingView.clearAnimation(); - loadingView.setVisibility(View.GONE); - <span class="keyword">switch</span> (refreshResult) - { - <span class="keyword">case</span> SUCCEED: - <span class="comment">// 加载成功</span> - loadStateImageView.setVisibility(View.VISIBLE); - loadStateTextView.setText(R.string.load_succeed); - loadStateImageView.setBackgroundResource(R.drawable.load_succeed); - <span class="keyword">break</span>; - <span class="keyword">case</span> FAIL: - <span class="keyword">default</span>: - <span class="comment">// 加载失败</span> - loadStateImageView.setVisibility(View.VISIBLE); - loadStateTextView.setText(R.string.load_fail); - loadStateImageView.setBackgroundResource(R.drawable.load_failed); - <span class="keyword">break</span>; - } - <span class="comment">// 刷新结果停留1秒</span> - <span class="keyword">new</span> Handler() - { - <span class="annotation">@Override</span> - <span class="keyword">public</span> <span class="keyword">void</span> handleMessage(Message msg) - { - changeState(DONE); - hide(); - } - }.sendEmptyMessageDelayed(<span class="number"></span>, <span class="number">1000</span>); - } - - <span class="keyword">private</span> <span class="keyword">void</span> changeState(<span class="keyword">int</span> to) - { - state = to; - <span class="keyword">switch</span> (state) - { - <span class="keyword">case</span> INIT: - <span class="comment">// 下拉布局初始状态</span> - refreshStateImageView.setVisibility(View.GONE); - refreshStateTextView.setText(R.string.pull_to_refresh); - pullView.clearAnimation(); - pullView.setVisibility(View.VISIBLE); - <span class="comment">// 上拉布局初始状态</span> - loadStateImageView.setVisibility(View.GONE); - loadStateTextView.setText(R.string.pullup_to_load); - pullUpView.clearAnimation(); - pullUpView.setVisibility(View.VISIBLE); - <span class="keyword">break</span>; - <span class="keyword">case</span> RELEASE_TO_REFRESH: - <span class="comment">// 释放刷新状态</span> - refreshStateTextView.setText(R.string.release_to_refresh); - pullView.startAnimation(rotateAnimation); - <span class="keyword">break</span>; - <span class="keyword">case</span> REFRESHING: - <span class="comment">// 正在刷新状态</span> - pullView.clearAnimation(); - refreshingView.setVisibility(View.VISIBLE); - pullView.setVisibility(View.INVISIBLE); - refreshingView.startAnimation(refreshingAnimation); - refreshStateTextView.setText(R.string.refreshing); - <span class="keyword">break</span>; - <span class="keyword">case</span> RELEASE_TO_LOAD: - <span class="comment">// 释放加载状态</span> - loadStateTextView.setText(R.string.release_to_load); - pullUpView.startAnimation(rotateAnimation); - <span class="keyword">break</span>; - <span class="keyword">case</span> LOADING: - <span class="comment">// 正在加载状态</span> - pullUpView.clearAnimation(); - loadingView.setVisibility(View.VISIBLE); - pullUpView.setVisibility(View.INVISIBLE); - loadingView.startAnimation(refreshingAnimation); - loadStateTextView.setText(R.string.loading); - <span class="keyword">break</span>; - <span class="keyword">case</span> DONE: - <span class="comment">// 刷新或加载完毕,啥都不做</span> - <span class="keyword">break</span>; - } - } - - <span class="comment">/**</span> - <span class="comment"> * 不限制上拉或下拉</span> - <span class="comment"> */</span> - <span class="keyword">private</span> <span class="keyword">void</span> releasePull() - { - canPullDown = <span class="keyword">true</span>; - canPullUp = <span class="keyword">true</span>; - } - - <span class="comment">/*</span> - <span class="comment"> * (非 Javadoc)由父控件决定是否分发事件,防止事件冲突</span> - <span class="comment"> * </span> - <span class="comment"> * @see android.view.ViewGroup#dispatchTouchEvent(android.view.MotionEvent)</span> - <span class="comment"> */</span> - <span class="annotation">@Override</span> - <span class="keyword">public</span> <span class="keyword">boolean</span> dispatchTouchEvent(MotionEvent ev) - { - <span class="keyword">switch</span> (ev.getActionMasked()) - { - <span class="keyword">case</span> MotionEvent.ACTION_DOWN: - downY = ev.getY(); - lastY = downY; - timer.cancel(); - mEvents = <span class="number"></span>; - releasePull(); - <span class="keyword">break</span>; - <span class="keyword">case</span> MotionEvent.ACTION_POINTER_DOWN: - <span class="keyword">case</span> MotionEvent.ACTION_POINTER_UP: - <span class="comment">// 过滤多点触碰</span> - <span class="keyword">break</span>; - <span class="keyword">case</span> MotionEvent.ACTION_MOVE: - <span class="keyword">if</span> (mEvents == <span class="number"></span>) - { - <span class="keyword">if</span> (((Pullable) pullableView).canPullDown() && canPullDown - && state != LOADING) - { - <span class="comment">// 可以下拉,正在加载时不能下拉</span> - <span class="comment">// 对实际滑动距离做缩小,造成用力拉的感觉</span> - <span class="keyword">if</span> (pullDownY < <span class="number"></span>) - { - pullDownY = <span class="number"></span>; - canPullDown = <span class="keyword">false</span>; - canPullUp = <span class="keyword">true</span>; - } - <span class="keyword">if</span> (pullDownY > getMeasuredHeight()) - pullDownY = getMeasuredHeight(); - <span class="keyword">if</span> (state == REFRESHING) - { - <span class="comment">// 正在刷新的时候触摸移动</span> - isTouch = <span class="keyword">true</span>; - } - } <span class="keyword">else</span> <span class="keyword">if</span> (((Pullable) pullableView).canPullUp() && canPullUp - && state != REFRESHING) - { - <span class="comment">// 可以上拉,正在刷新时不能上拉</span> - <span class="keyword">if</span> (pullUpY > <span class="number"></span>) - { - pullUpY = <span class="number"></span>; - canPullDown = <span class="keyword">true</span>; - canPullUp = <span class="keyword">false</span>; - } - <span class="keyword">if</span> (pullUpY < -getMeasuredHeight()) - pullUpY = -getMeasuredHeight(); - <span class="keyword">if</span> (state == LOADING) - { - <span class="comment">// 正在加载的时候触摸移动</span> - isTouch = <span class="keyword">true</span>; - } - } <span class="keyword">else</span> - releasePull(); - } <span class="keyword">else</span> - mEvents = <span class="number"></span>; - lastY = ev.getY(); - <span class="comment">// 根据下拉距离改变比例</span> - radio = (<span class="keyword">float</span>) (<span class="number">2</span> + <span class="number">2</span> * Math.tan(Math.PI / <span class="number">2</span> / getMeasuredHeight() - * (pullDownY + Math.abs(pullUpY)))); - requestLayout(); - <span class="keyword">if</span> (pullDownY <= refreshDist - <span style=<span class="string">&#8220;white-space:pre&#8221;</span>> </span>&& (state == RELEASE_TO_REFRESH || state == DONE)) { - <span style=<span class="string">&#8220;white-space:pre&#8221;</span>> </span><span class="comment">// 如果下拉距离没达到刷新的距离且当前状态是释放刷新,改变状态为下拉刷新</span> - <span style=<span class="string">&#8220;white-space:pre&#8221;</span>> </span>changeState(INIT); - <span style=<span class="string">&#8220;white-space:pre&#8221;</span>> </span>} - <span style=<span class="string">&#8220;white-space:pre&#8221;</span>> </span><span class="keyword">if</span> (pullDownY >= refreshDist && (state == INIT || state == DONE)) { - <span style=<span class="string">&#8220;white-space:pre&#8221;</span>> </span><span class="comment">// 如果下拉距离达到刷新的距离且当前状态是初始状态刷新,改变状态为释放刷新</span> - <span style=<span class="string">&#8220;white-space:pre&#8221;</span>> </span>changeState(RELEASE_TO_REFRESH); - <span style=<span class="string">&#8220;white-space:pre&#8221;</span>> </span>} - <span style=<span class="string">&#8220;white-space:pre&#8221;</span>> </span><span class="comment">// 下面是判断上拉加载的,同上,注意pullUpY是负值</span> - <span style=<span class="string">&#8220;white-space:pre&#8221;</span>> </span><span class="keyword">if</span> (-pullUpY <= loadmoreDist - <span style=<span class="string">&#8220;white-space:pre&#8221;</span>> </span>&& (state == RELEASE_TO_LOAD || state == DONE)) { - <span style=<span class="string">&#8220;white-space:pre&#8221;</span>> </span>changeState(INIT); - <span style=<span class="string">&#8220;white-space:pre&#8221;</span>> </span>} - <span style=<span class="string">&#8220;white-space:pre&#8221;</span>> </span><span class="keyword">if</span> (-pullUpY >= loadmoreDist && (state == INIT || state == DONE)) { - <span style=<span class="string">&#8220;white-space:pre&#8221;</span>> </span>changeState(RELEASE_TO_LOAD); - <span style=<span class="string">&#8220;white-space:pre&#8221;</span>> </span>} - <span class="comment">// 因为刷新和加载操作不能同时进行,所以pullDownY和pullUpY不会同时不为0,因此这里用(pullDownY +</span> - <span class="comment">// Math.abs(pullUpY))就可以不对当前状态作区分了</span> - <span class="keyword">if</span> ((pullDownY + Math.abs(pullUpY)) > <span class="number">8</span>) - { - <span class="comment">// 防止下拉过程中误触发长按事件和点击事件</span> - ev.setAction(MotionEvent.ACTION_CANCEL); - } - <span class="keyword">break</span>; - <span class="keyword">case</span> MotionEvent.ACTION_UP: - <span class="keyword">if</span> (pullDownY > refreshDist || -pullUpY > loadmoreDist) - <span class="comment">// 正在刷新时往下拉(正在加载时往上拉),释放后下拉头(上拉头)不隐藏</span> - isTouch = <span class="keyword">false</span>; - <span class="keyword">if</span> (state == RELEASE_TO_REFRESH) - { - changeState(REFRESHING); - <span class="comment">// 刷新操作</span> - <span class="keyword">if</span> (mListener != <span class="keyword">null</span>) - mListener.onRefresh(<span class="keyword">this</span>); - } <span class="keyword">else</span> <span class="keyword">if</span> (state == RELEASE_TO_LOAD) - { - changeState(LOADING); - <span class="comment">// 加载操作</span> - <span class="keyword">if</span> (mListener != <span class="keyword">null</span>) - mListener.onLoadMore(<span class="keyword">this</span>); - } - hide(); - <span class="keyword">default</span>: - <span class="keyword">break</span>; - } - <span class="comment">// 事件分发交给父类</span> - <span class="keyword">super</span>.dispatchTouchEvent(ev); - <span class="keyword">return</span> <span class="keyword">true</span>; - } - - <span class="keyword">private</span> <span class="keyword">void</span> initView() - { - <span class="comment">// 初始化下拉布局</span> - pullView = refreshView.findViewById(R.id.pull_icon); - refreshStateTextView = (TextView) refreshView - .findViewById(R.id.state_tv); - refreshingView = refreshView.findViewById(R.id.refreshing_icon); - refreshStateImageView = refreshView.findViewById(R.id.state_iv); - <span class="comment">// 初始化上拉布局</span> - pullUpView = loadmoreView.findViewById(R.id.pullup_icon); - loadStateTextView = (TextView) loadmoreView - .findViewById(R.id.loadstate_tv); - loadingView = loadmoreView.findViewById(R.id.loading_icon); - loadStateImageView = loadmoreView.findViewById(R.id.loadstate_iv); - } - - <span class="annotation">@Override</span> - <span class="keyword">protected</span> <span class="keyword">void</span> onLayout(<span class="keyword">boolean</span> changed, <span class="keyword">int</span> l, <span class="keyword">int</span> t, <span class="keyword">int</span> r, <span class="keyword">int</span> b) - { - <span class="keyword">if</span> (!isLayout) - { - <span class="comment">// 这里是第一次进来的时候做一些初始化</span> - refreshView = getChildAt(<span class="number"></span>); - pullableView = getChildAt(<span class="number">1</span>); - loadmoreView = getChildAt(<span class="number">2</span>); - isLayout = <span class="keyword">true</span>; - initView(); - refreshDist = ((ViewGroup) refreshView).getChildAt(<span class="number"></span>) - .getMeasuredHeight(); - loadmoreDist = ((ViewGroup) loadmoreView).getChildAt(<span class="number"></span>) - .getMeasuredHeight(); - } - <span class="comment">// 改变子控件的布局,这里直接用(pullDownY + pullUpY)作为偏移量,这样就可以不对当前状态作区分</span> - refreshView.layout(<span class="number"></span>, - refreshView.getMeasuredWidth(), (<span class="keyword">int</span>) (pullDownY + pullUpY)); - pullableView.layout(<span class="number"></span>, (<span class="keyword">int</span>) (pullDownY + pullUpY), - pullableView.getMeasuredWidth(), (<span class="keyword">int</span>) (pullDownY + pullUpY) - + pullableView.getMeasuredHeight()); - loadmoreView.layout(<span class="number"></span>, - (<span class="keyword">int</span>) (pullDownY + pullUpY) + pullableView.getMeasuredHeight(), - loadmoreView.getMeasuredWidth(), - (<span class="keyword">int</span>) (pullDownY + pullUpY) + pullableView.getMeasuredHeight() - + loadmoreView.getMeasuredHeight()); - } - - <span class="keyword">class</span> MyTimer - { - <span class="keyword">private</span> Handler handler; - <span class="keyword">private</span> Timer timer; - <span class="keyword">private</span> MyTask mTask; - - <span class="keyword">public</span> MyTimer(Handler handler) - { - <span class="keyword">this</span>.handler = handler; - timer = <span class="keyword">new</span> Timer(); - } - - <span class="keyword">public</span> <span class="keyword">void</span> schedule(<span class="keyword">long</span> period) - { - <span class="keyword">if</span> (mTask != <span class="keyword">null</span>) - { - mTask.cancel(); - mTask = <span class="keyword">null</span>; - } - mTask = <span class="keyword">new</span> MyTask(handler); - timer.schedule(mTask, <span class="number"></span>, period); - } - - <span class="keyword">public</span> <span class="keyword">void</span> cancel() - { - <span class="keyword">if</span> (mTask != <span class="keyword">null</span>) - { - mTask.cancel(); - mTask = <span class="keyword">null</span>; - } - } - - <span class="keyword">class</span> MyTask <span class="keyword">extends</span> TimerTask - { - <span class="keyword">private</span> Handler handler; - - <span class="keyword">public</span> MyTask(Handler handler) - { - <span class="keyword">this</span>.handler = handler; - } - - <span class="annotation">@Override</span> - <span class="keyword">public</span> <span class="keyword">void</span> run() - { - handler.obtainMessage().sendToTarget(); - } - - } - } - - <span class="comment">/**</span> - <span class="comment"> * 刷新加载回调接口</span> - <span class="comment"> * </span> - <span class="comment"> * @author chenjing</span> - <span class="comment"> * </span> - <span class="comment"> */</span> - <span class="keyword">public</span> <span class="keyword">interface</span> OnRefreshListener - { - <span class="comment">/**</span> - <span class="comment"> * 刷新操作</span> - <span class="comment"> */</span> - <span class="keyword">void</span> onRefresh(PullToRefreshLayout pullToRefreshLayout); - - <span class="comment">/**</span> - <span class="comment"> * 加载操作</span> - <span class="comment"> */</span> - <span class="keyword">void</span> onLoadMore(PullToRefreshLayout pullToRefreshLayout); - } - - } </div> 上面就是整个布局的代码,并不是很难。下面看各个View对Pullable接口的实现,ListView和GridView还有ExpandableListView的判断方法是一样的: PullableListView: <div class="dp-highlighter bg_java"> <div class="bar"> <div class="tools"> **[java]** [view plain](http://blog.csdn.net/zhongkejingwang/article/details/38868463#)<span class="tracking-ad" data-mod="popu_168"><span class="tracking-ad" data-mod="popu_168"> [copy](http://blog.csdn.net/zhongkejingwang/article/details/38868463#)</span></span> <div> <embed id="ZeroClipboardMovie_3" src="http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf" type="application/x-shockwave-flash" width="18" height="18" align="middle" name="ZeroClipboardMovie_3"> </embed> </div> </div> </div> - <span class="keyword">package</span> com.jingchen.pulltorefresh.pullableview; - - <span class="keyword">import</span> android.content.Context; - <span class="keyword">import</span> android.util.AttributeSet; - <span class="keyword">import</span> android.util.Log; - <span class="keyword">import</span> android.widget.ListView; - - <span class="keyword">public</span> <span class="keyword">class</span> PullableListView <span class="keyword">extends</span> ListView <span class="keyword">implements</span> Pullable - { - - <span class="keyword">public</span> PullableListView(Context context) - { - <span class="keyword">super</span>(context); - } - - <span class="keyword">public</span> PullableListView(Context context, AttributeSet attrs) - { - <span class="keyword">super</span>(context, attrs); - } - - <span class="keyword">public</span> PullableListView(Context context, AttributeSet attrs, <span class="keyword">int</span> defStyle) - { - <span class="keyword">super</span>(context, attrs, defStyle); - } - - <span class="annotation">@Override</span> - <span class="keyword">public</span> <span class="keyword">boolean</span> canPullDown() - { - <span class="keyword">if</span> (getCount() == <span class="number"></span>) - { - <span class="comment">// 没有item的时候也可以下拉刷新</span> - <span class="keyword">return</span> <span class="keyword">true</span>; - } <span class="keyword">else</span> <span class="keyword">if</span> (getFirstVisiblePosition() == <span class="number"></span> - && getChildAt(<span class="number"></span>).getTop() >= <span class="number"></span>) - { - <span class="comment">// 滑到ListView的顶部了</span> - <span class="keyword">return</span> <span class="keyword">true</span>; - } <span class="keyword">else</span> - <span class="keyword">return</span> <span class="keyword">false</span>; - } - - <span class="annotation">@Override</span> - <span class="keyword">public</span> <span class="keyword">boolean</span> canPullUp() - { - <span class="keyword">if</span> (getCount() == <span class="number"></span>) - { - <span class="comment">// 没有item的时候也可以上拉加载</span> - <span class="keyword">return</span> <span class="keyword">true</span>; - { - <span class="comment">// 滑到底部了</span> - && getChildAt( - getLastVisiblePosition() - <span class="keyword">return</span> <span class="keyword">true</span>; - } - <span class="keyword">return</span> <span class="keyword">false</span>; - } - } </div> PullableExpandableListView: <div class="dp-highlighter bg_java"> <div class="bar"> <div class="tools"> **[java]** [view plain](http://blog.csdn.net/zhongkejingwang/article/details/38868463#)<span class="tracking-ad" data-mod="popu_168"><span class="tracking-ad" data-mod="popu_168"> [copy](http://blog.csdn.net/zhongkejingwang/article/details/38868463#)</span></span> <div> <embed id="ZeroClipboardMovie_4" src="http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf" type="application/x-shockwave-flash" width="18" height="18" align="middle" name="ZeroClipboardMovie_4"> </embed> </div> </div> </div> - <span class="keyword">package</span> com.jingchen.pulltorefresh.pullableview; - - <span class="keyword">import</span> android.content.Context; - <span class="keyword">import</span> android.util.AttributeSet; - <span class="keyword">import</span> android.widget.ExpandableListView; - - <span class="keyword">public</span> <span class="keyword">class</span> PullableExpandableListView <span class="keyword">extends</span> ExpandableListView <span class="keyword">implements</span> - Pullable - { - - <span class="keyword">public</span> PullableExpandableListView(Context context) - { - <span class="keyword">super</span>(context); - } - - <span class="keyword">public</span> PullableExpandableListView(Context context, AttributeSet attrs) - { - <span class="keyword">super</span>(context, attrs); - } - - <span class="keyword">public</span> PullableExpandableListView(Context context, AttributeSet attrs, - <span class="keyword">int</span> defStyle) - { - <span class="keyword">super</span>(context, attrs, defStyle); - } - - <span class="annotation">@Override</span> - <span class="keyword">public</span> <span class="keyword">boolean</span> canPullDown() - { - <span class="keyword">if</span> (getCount() == <span class="number"></span>) - { - <span class="comment">// 没有item的时候也可以下拉刷新</span> - <span class="keyword">return</span> <span class="keyword">true</span>; - } <span class="keyword">else</span> <span class="keyword">if</span> (getFirstVisiblePosition() == <span class="number"></span> - && getChildAt(<span class="number"></span>).getTop() >= <span class="number"></span>) - { - <span class="comment">// 滑到顶部了</span> - <span class="keyword">return</span> <span class="keyword">true</span>; - } <span class="keyword">else</span> - <span class="keyword">return</span> <span class="keyword">false</span>; - } - - <span class="annotation">@Override</span> - <span class="keyword">public</span> <span class="keyword">boolean</span> canPullUp() - { - <span class="keyword">if</span> (getCount() == <span class="number"></span>) - { - <span class="comment">// 没有item的时候也可以上拉加载</span> - <span class="keyword">return</span> <span class="keyword">true</span>; - { - <span class="comment">// 滑到底部了</span> - && getChildAt( - getLastVisiblePosition() - <span class="keyword">return</span> <span class="keyword">true</span>; - } - <span class="keyword">return</span> <span class="keyword">false</span>; - } - - } </div> PullableGridView: <div class="dp-highlighter bg_java"> <div class="bar"> <div class="tools"> **[java]** [view plain](http://blog.csdn.net/zhongkejingwang/article/details/38868463#)<span class="tracking-ad" data-mod="popu_168"><span class="tracking-ad" data-mod="popu_168"> [copy](http://blog.csdn.net/zhongkejingwang/article/details/38868463#)</span></span> <div> <embed id="ZeroClipboardMovie_5" src="http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf" type="application/x-shockwave-flash" width="18" height="18" align="middle" name="ZeroClipboardMovie_5"> </embed> </div> </div> </div> - <span class="keyword">package</span> com.jingchen.pulltorefresh.pullableview; - - <span class="keyword">import</span> android.content.Context; - <span class="keyword">import</span> android.util.AttributeSet; - <span class="keyword">import</span> android.widget.GridView; - - <span class="keyword">public</span> <span class="keyword">class</span> PullableGridView <span class="keyword">extends</span> GridView <span class="keyword">implements</span> Pullable - { - - <span class="keyword">public</span> PullableGridView(Context context) - { - <span class="keyword">super</span>(context); - } - - <span class="keyword">public</span> PullableGridView(Context context, AttributeSet attrs) - { - <span class="keyword">super</span>(context, attrs); - } - - <span class="keyword">public</span> PullableGridView(Context context, AttributeSet attrs, <span class="keyword">int</span> defStyle) - { - <span class="keyword">super</span>(context, attrs, defStyle); - } - - <span class="annotation">@Override</span> - <span class="keyword">public</span> <span class="keyword">boolean</span> canPullDown() - { - <span class="keyword">if</span> (getCount() == <span class="number"></span>) - { - <span class="comment">// 没有item的时候也可以下拉刷新</span> - <span class="keyword">return</span> <span class="keyword">true</span>; - } <span class="keyword">else</span> <span class="keyword">if</span> (getFirstVisiblePosition() == <span class="number"></span> - && getChildAt(<span class="number"></span>).getTop() >= <span class="number"></span>) - { - <span class="comment">// 滑到顶部了</span> - <span class="keyword">return</span> <span class="keyword">true</span>; - } <span class="keyword">else</span> - <span class="keyword">return</span> <span class="keyword">false</span>; - } - - <span class="annotation">@Override</span> - <span class="keyword">public</span> <span class="keyword">boolean</span> canPullUp() - { - <span class="keyword">if</span> (getCount() == <span class="number"></span>) - { - <span class="comment">// 没有item的时候也可以上拉加载</span> - <span class="keyword">return</span> <span class="keyword">true</span>; - { - <span class="comment">// 滑到底部了</span> - && getChildAt( - getLastVisiblePosition() - <span class="keyword">return</span> <span class="keyword">true</span>; - } - <span class="keyword">return</span> <span class="keyword">false</span>; - } - - } </div> PullableScrollView: <div class="dp-highlighter bg_java"> <div class="bar"> <div class="tools"> **[java]** [view plain](http://blog.csdn.net/zhongkejingwang/article/details/38868463#)<span class="tracking-ad" data-mod="popu_168"><span class="tracking-ad" data-mod="popu_168"> [copy](http://blog.csdn.net/zhongkejingwang/article/details/38868463#)</span></span> <div> <embed id="ZeroClipboardMovie_6" src="http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf" type="application/x-shockwave-flash" width="18" height="18" align="middle" name="ZeroClipboardMovie_6"> </embed> </div> </div> </div> - <span class="keyword">package</span> com.jingchen.pulltorefresh.pullableview; - - <span class="keyword">import</span> android.content.Context; - <span class="keyword">import</span> android.util.AttributeSet; - <span class="keyword">import</span> android.widget.ScrollView; - - <span class="keyword">public</span> <span class="keyword">class</span> PullableScrollView <span class="keyword">extends</span> ScrollView <span class="keyword">implements</span> Pullable - { - - <span class="keyword">public</span> PullableScrollView(Context context) - { - <span class="keyword">super</span>(context); - } - - <span class="keyword">public</span> PullableScrollView(Context context, AttributeSet attrs) - { - <span class="keyword">super</span>(context, attrs); - } - - <span class="keyword">public</span> PullableScrollView(Context context, AttributeSet attrs, <span class="keyword">int</span> defStyle) - { - <span class="keyword">super</span>(context, attrs, defStyle); - } - - <span class="annotation">@Override</span> - <span class="keyword">public</span> <span class="keyword">boolean</span> canPullDown() - { - <span class="keyword">if</span> (getScrollY() == <span class="number"></span>) - <span class="keyword">return</span> <span class="keyword">true</span>; - <span class="keyword">else</span> - <span class="keyword">return</span> <span class="keyword">false</span>; - } - - <span class="annotation">@Override</span> - <span class="keyword">public</span> <span class="keyword">boolean</span> canPullUp() - { - <span class="keyword">return</span> <span class="keyword">true</span>; - <span class="keyword">else</span> - <span class="keyword">return</span> <span class="keyword">false</span>; - } - - } </div> PullableWebView: <div class="dp-highlighter bg_java"> <div class="bar"> <div class="tools"> **[java]** [view plain](http://blog.csdn.net/zhongkejingwang/article/details/38868463#)<span class="tracking-ad" data-mod="popu_168"><span class="tracking-ad" data-mod="popu_168"> [copy](http://blog.csdn.net/zhongkejingwang/article/details/38868463#)</span></span> <div> <embed id="ZeroClipboardMovie_7" src="http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf" type="application/x-shockwave-flash" width="18" height="18" align="middle" name="ZeroClipboardMovie_7"> </embed> </div> </div> </div> - <span class="keyword">package</span> com.jingchen.pulltorefresh.pullableview; - - <span class="keyword">import</span> android.content.Context; - <span class="keyword">import</span> android.util.AttributeSet; - <span class="keyword">import</span> android.webkit.WebView; - - <span class="keyword">public</span> <span class="keyword">class</span> PullableWebView <span class="keyword">extends</span> WebView <span class="keyword">implements</span> Pullable - { - - <span class="keyword">public</span> PullableWebView(Context context) - { - <span class="keyword">super</span>(context); - } - - <span class="keyword">public</span> PullableWebView(Context context, AttributeSet attrs) - { - <span class="keyword">super</span>(context, attrs); - } - - <span class="keyword">public</span> PullableWebView(Context context, AttributeSet attrs, <span class="keyword">int</span> defStyle) - { - <span class="keyword">super</span>(context, attrs, defStyle); - } - - <span class="annotation">@Override</span> - <span class="keyword">public</span> <span class="keyword">boolean</span> canPullDown() - { - <span class="keyword">if</span> (getScrollY() == <span class="number"></span>) - <span class="keyword">return</span> <span class="keyword">true</span>; - <span class="keyword">else</span> - <span class="keyword">return</span> <span class="keyword">false</span>; - } - - <span class="annotation">@Override</span> - <span class="keyword">public</span> <span class="keyword">boolean</span> canPullUp() - { - <span class="keyword">if</span> (getScrollY() >= getContentHeight() * getScale() - <span class="keyword">return</span> <span class="keyword">true</span>; - <span class="keyword">else</span> - <span class="keyword">return</span> <span class="keyword">false</span>; - } - } </div> ImageView和TextView就不贴了,我直接在方法里返回了true。OK了,整个demo的代码有点多,就不贴了。 [源码下载](http://download.csdn.net/detail/zhongkejingwang/7828061)

2016年4月13日 · 17 分钟 · 天边的星星

win10 android studio Terminal无法输入

win R -> 输入 cmd 回车 ,打开命令行界面 在标题栏上右键,属性 勾选使用旧版控制台 ![](http://upload-images.jianshu.io/upload_images/1181400-c3c27b0c43937ea3.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 转自:http://www.jianshu.com/p/0ed41f13fd6a

2016年4月12日 · 1 分钟 · 天边的星星

Android 高清加载巨图方案 拒绝压缩图片

Android 高清加载巨图方案 拒绝压缩图片 转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/49300989; 一、概述 距离上一篇博客有段时间没更新了,主要是最近有些私事导致的,那么就先来一篇简单一点的博客脉动回来。 对于加载图片,大家都不陌生,一般为了尽可能避免OOM都会按照如下做法: 对于图片显示:根据需要显示图片控件的大小对图片进行压缩显示。 如果图片数量非常多:则会使用LruCache等缓存机制,将所有图片占据的内容维持在一个范围内。 其实对于图片加载还有种情况,就是单个图片非常巨大,并且还不允许压缩。比如显示:世界地图、清明上河图、微博长图等。 那么对于这种需求,该如何做呢? 首先不压缩,按照原图尺寸加载,那么屏幕肯定是不够大的,并且考虑到内存的情况,不可能一次性整图加载到内存中,所以肯定是局部加载,那么就需要用到一个类: BitmapRegionDecoder 其次,既然屏幕显示不完,那么最起码要添加一个上下左右拖动的手势,让用户可以拖动查看。 那么综上,本篇博文的目的就是去自定义一个显示巨图的View,支持用户去拖动查看,大概的效果图如下: 好吧,这清明上河图太长了,想要观看全图,文末下载,图片在assets目录。当然如果你的图,高度也很大,肯定也是可以上下拖动的。 二、初识BitmapRegionDecoder BitmapRegionDecoder主要用于显示图片的某一块矩形区域,如果你需要显示某个图片的指定区域,那么这个类非常合适。 对于该类的用法,非常简单,既然是显示图片的某一块区域,那么至少只需要一个方法去设置图片;一个方法传入显示的区域即可;详见: BitmapRegionDecoder提供了一系列的newInstance方法来构造对象,支持传入文件路径,文件描述符,文件的inputstrem等。例如: ``` BitmapRegionDecoder bitmapRegionDecoder = BitmapRegionDecoder.newInstance(inputStream, &lt;span class="hljs-keyword">false&lt;/span>); * 上述解决了传入我们需要处理的图片,那么接下来就是显示指定的区域。 ``` `bitmapRegionDecoder.decodeRegion(rect, options);` 参数一很明显是一个rect,参数二是BitmapFactory.Options,你可以控制图片的`inSampleSize`,`inPreferredConfig`等。 那么下面看一个超级简单的例子: ``` `<span class=“hljs-keyword”>package</span> com.zhy.blogcodes.largeImage; <span class=“hljs-keyword”>import</span> android.graphics.Bitmap; <span class=“hljs-keyword”>import</span> android.graphics.BitmapFactory; <span class=“hljs-keyword”>import</span> android.graphics.BitmapRegionDecoder; <span class=“hljs-keyword”>import</span> android.graphics.Rect; <span class=“hljs-keyword”>import</span> android.os.Bundle; <span class=“hljs-keyword”>import</span> android.support.v7.app.AppCompatActivity; <span class=“hljs-keyword”>import</span> android.widget.ImageView; <span class=“hljs-keyword”>import</span> com.zhy.blogcodes.R; <span class=“hljs-keyword”>import</span> java.io.IOException; <span class=“hljs-keyword”>import</span> java.io.InputStream; ...

2016年4月6日 · 7 分钟 · 天边的星星

Android加载长图那些事

对于图片加载有一种这样的情况,就是单个图片非常巨大,并且还不允许压缩。比如显示:世界地图、微博长图等。首先不压缩,按照原图尺寸加载,那么屏幕肯定是不够大的,并且考虑到内存的情况,不可能一次性整图加载到内存中,所以肯定是局部加载。这就需要用到Api提供的这个类:BitmapRegionDecoder。 ### <a name="t1"></a>BitmapRegionDecoder 我们来看一下官方的介绍: ``` `BitmapRegionDecoder can be used <span class=“hljs-built_in”>to</span> decode <span class=“hljs-operator”>a</span> rectangle region <span class=“hljs-built_in”>from</span> <span class=“hljs-operator”>an</span> image. BitmapRegionDecoder is particularly useful when <span class=“hljs-operator”>an</span> original image is large <span class=“hljs-operator”>and</span> you only need parts <span class=“hljs-operator”>of</span> <span class=“hljs-operator”>the</span> image. To <span class=“hljs-built_in”>create</span> <span class=“hljs-operator”>a</span> BitmapRegionDecoder, call newInstance(…). Given <span class=“hljs-operator”>a</span> BitmapRegionDecoder, users can call decodeRegion() repeatedly <span class=“hljs-built_in”>to</span> <span class=“hljs-built_in”>get</span> <span class=“hljs-operator”>a</span> decoded Bitmap <span class=“hljs-operator”>of</span> <span class=“hljs-operator”>the</span> specified region.` ...

2016年4月6日 · 2 分钟 · 天边的星星

Android自定义控件系列二:自定义开关按钮(一)

这一次我们将会实现一个完整纯粹的自定义控件,而不是像之前的组合控件一样,拿系统的控件来实现;计划分为三部分:自定义控件的基本部分,自定义控件的触摸事件的处理和自定义控件的自定义属性; 下面就开始第一部分的编写,本次以一个定义的开关按钮为例,下面就开始吧: 先看看效果,一个点击开关按钮,实现点击切换开关状态: 为了能够讲解清晰,还是来一些基本的介绍。 首先需要明确的就是自定义控件还是继承自View这个类,Google在View这个类里面提供了相当多的方法供我们使用,使用这些方法我们可以实现相当多的效果和功能,在这里需要用到几个主要的方法; 自定义控件的步骤、用到的主要方法: 1、首先需要定义一个类,继承自View;对于继承View的类,会需要实现至少一个构造方法;实际上这里一共有三个构造方法: **public View (Context context)**是在java代码创建视图的时候被调用(使用new的方式),如果是从xml填充的视图,就不会调用这个 **public View (Context context, AttributeSet attrs)**这个是在xml创建但是没有指定style的时候被调用 **public View (Context context, AttributeSet attrs, int defStyle)**这个是在第二个基础上添加style的时候被调用的 所以对于这里来说,如果不使用style, 我们重点关注第二个构造方法即可 2、对于任何一个控件来说,它需要显示在我们的界面上,那么肯定需要定义它的大小; 在这里Google提供了一个方法:**protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec);我们去看这个方法的内部,实际上是调用了protected final void setMeasuredDimension(int measuredWidth, int measuredHeight);**这个方法,其中第一个参数是view的宽,第二个参数是view的高,这样我们就可以设置view的宽高了,但是要注意,这样设置的单位都是像素 3、对于一个需要显示的控件来说,我们往往还需要确定它的位置: 这就要求重写onLayout方法;但是实际上这个方法在自定义view的时候使用的不多,原因是因为对于位置来说,控件只有建议权而没有决定权,决定权一般在父控件那里。 4、对于一个控件,需要显示,我们当然需要将它绘制出来,这里就需要重写onDraw方法,来将这个控件绘制出来 5、当控件状态改变的时候,我们很可能需要刷新view的显示状态,这时候就需要调用invalidate()方法,这个方法实际上会重新调用onDraw方法来重绘控件 6、在定义控件的过程中,如果需要对view设置点击事件,可以直接使用setOnClickListener方法,而不需要写view.setOnClickListener; **7、在布局文件中将这个自定义控件定义出来,注意名字要使用全类名;**而且,由于是继承自view控件,所以在xml文件中如果是view本身的属性都可以直接使用,比如:android:layout_width等等 这里比较关键的地方就在于这个onDraw方法,我们一起来看一下: **[java]** [view plain](http://blog.csdn.net/cyp331203/article/details/40736027#) [copy](http://blog.csdn.net/cyp331203/article/details/40736027#) <div> <embed id="ZeroClipboardMovie_1" src="http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf" type="application/x-shockwave-flash" width="18" height="18" align="middle" name="ZeroClipboardMovie_1"> </embed> </div> <span class="tracking-ad" data-mod="popu_167">[![在CODE上查看代码片](https://code.csdn.net/assets/CODE_ico.png)](https://code.csdn.net/snippets/505895)</span><span class="tracking-ad" data-mod="popu_170">[![派生到我的代码片](https://code.csdn.net/assets/ico_fork.svg)](https://code.csdn.net/snippets/505895/fork)</span></div> </div> - <span class="comment">/**</span> - <span class="comment"> * 画view的方法,绘制当前view的内容</span> - <span class="comment"> */</span> - <span class="annotation">@Override</span> - <span class="keyword">protected</span> <span class="keyword">void</span> onDraw(Canvas canvas) { - <span class="comment">// super.onDraw(canvas);</span> - - - Paint paint = <span class="keyword">new</span> Paint(); - <span class="comment">// 打开抗锯齿</span> - paint.setAntiAlias(<span class="keyword">true</span>); - - - <span class="comment">// 画背景</span> - canvas.drawBitmap(backgroundBitmap, <span class="number"></span>, <span class="number"></span>, paint); - <span class="comment">// 画滑块</span> - canvas.drawBitmap(slideButton, slideBtn_left, <span class="number"></span>, paint); - } </div> &nbsp; **onDraw**方法传入的参数是一个**Canvas**画布对象,这个实际上跟Java中的差不太多,我们要在画布上画画也需要一个画笔,我们这里也将其初始化出来**Paint paint = new Paint()**,同时设置了一个抗锯齿效果**paint.setAntiAlias(true)**,然后调用**drawBitmap**的方法,先后绘制了开关的背景和开关的滑块,分别入下图: ![](http://img.blog.csdn.net/20141103103403203?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY3lwMzMxMjAz/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center) ![](http://img.blog.csdn.net/20141103103342450?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY3lwMzMxMjAz/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center) 这里要注意的一点就是,**drawBitmap(Bitmap bitmap, float left, float top, Paint paint)**方法中间的两个float类型的参数,分别代表绘制图形的左上角的x和y的坐标(原点设置在左上角),所以这里如果我们个绘制坐标都传入0,0,那么开关会处在一个关的状态,这里,我们对于滑块使用了一个变量**slideBtn_left**来设置其位置,**那么对于关闭状态,slideBtn_left的值就应该为0,对于开启状态,slideBtn_left的值就应该是backgroundBitmap(背景)的宽度减去slideButton(滑块)的宽度**; 那么这样一来,机制就比较清楚了,我们只需要在控件上设置一个点击事件,同时设置一个boolean变量代表开关的状态,当点击的时候,切换这个boolean类型的变量为true或者false,同时变化**slideButton**的值为****或者**backgroundBitmap.getWidth()-slideButton.getWidth()**,然后再调用**invalidate()**方法刷新控件,就可以实现基本的开关功能了 下面来看具体的代码,注解比较详细: 自定义控件的类MyToggleButton.java,继承自View: <div class="dp-highlighter bg_java"> <div class="bar"> <div class="tools"> **[java]** [view plain](http://blog.csdn.net/cyp331203/article/details/40736027#)<span class="tracking-ad" data-mod="popu_168"><span class="tracking-ad" data-mod="popu_168"> [copy](http://blog.csdn.net/cyp331203/article/details/40736027#)</span></span> <div> <embed id="ZeroClipboardMovie_2" src="http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf" type="application/x-shockwave-flash" width="18" height="18" align="middle" name="ZeroClipboardMovie_2"> </embed> </div> <span class="tracking-ad" data-mod="popu_167">[![在CODE上查看代码片](https://code.csdn.net/assets/CODE_ico.png)](https://code.csdn.net/snippets/505895)</span><span class="tracking-ad" data-mod="popu_170">[![派生到我的代码片](https://code.csdn.net/assets/ico_fork.svg)](https://code.csdn.net/snippets/505895/fork)</span></div> </div> - <span class="keyword">package</span> com.example.togglebutton.ui; - - <span class="keyword">import</span> com.example.togglebutton.R; - - <span class="keyword">import</span> android.content.Context; - <span class="keyword">import</span> android.graphics.Bitmap; - <span class="keyword">import</span> android.graphics.BitmapFactory; - <span class="keyword">import</span> android.graphics.Canvas; - <span class="keyword">import</span> android.graphics.Paint; - <span class="keyword">import</span> android.util.AttributeSet; - <span class="keyword">import</span> android.view.View; - - <span class="comment">/*</span> - <span class="comment"> * 自定义view的几个步骤:</span> - <span class="comment"> * 1、首先需要写一个类来继承自View</span> - <span class="comment"> * 2、需要得到view的对象,那么需要重写构造方法,其中一参的构造方法用于new,二参的构造方法用于xml布局文件使用,三参的构造方法可以传入一个样式</span> - <span class="comment"> * 3、需要设置view的大小,那么需要重写onMeasure方法</span> - <span class="comment"> * 4、需要设置view的位置,那么需要重写onLayout方法,但是这个方法在自定义view的时候用的不多,原因主要在于view的位置主要是由父控件来决定</span> - <span class="comment"> * 5、需要绘制出所需要显示的view,那么需要重写onDraw方法</span> - <span class="comment"> * 6、当控件状态改变的时候,需要重绘view,那么调用invalidate();方法,这个方法实际上会重新调用onDraw方法</span> - <span class="comment"> * 7、在这其中,如果需要对view设置点击事件,可以直接调用setOnClickListener方法</span> - <span class="comment"> */</span> - - <span class="keyword">public</span> <span class="keyword">class</span> MyToggleButton <span class="keyword">extends</span> View { - - <span class="comment">/**</span> - <span class="comment"> * 开关按钮的背景</span> - <span class="comment"> */</span> - <span class="keyword">private</span> Bitmap backgroundBitmap; - <span class="comment">/**</span> - <span class="comment"> * 开关按钮的滑动部分</span> - <span class="comment"> */</span> - <span class="keyword">private</span> Bitmap slideButton; - <span class="comment">/**</span> - <span class="comment"> * 滑动按钮的左边界</span> - <span class="comment"> */</span> - <span class="keyword">private</span> <span class="keyword">float</span> slideBtn_left; - <span class="comment">/**</span> - <span class="comment"> * 当前开关的状态</span> - <span class="comment"> */</span> - <span class="keyword">private</span> <span class="keyword">boolean</span> currentState = <span class="keyword">false</span>; - - <span class="comment">/**</span> - <span class="comment"> * 在代码里面创建对象的时候,使用此构造方法</span> - <span class="comment"> * </span> - <span class="comment"> * @param context</span> - <span class="comment"> */</span> - <span class="keyword">public</span> MyToggleButton(Context context) { - <span class="keyword">super</span>(context); - } - - <span class="comment">/**</span> - <span class="comment"> * 在布局文件中声明的view,创建时由系统自动调用</span> - <span class="comment"> * </span> - <span class="comment"> * @param context</span> - <span class="comment"> * @param attrs</span> - <span class="comment"> */</span> - <span class="keyword">public</span> MyToggleButton(Context context, AttributeSet attrs) { - <span class="keyword">super</span>(context, attrs); - initView(); - } - - <span class="comment">/**</span> - <span class="comment"> * 测量尺寸时的回调方法</span> - <span class="comment"> */</span> - <span class="annotation">@Override</span> - <span class="keyword">protected</span> <span class="keyword">void</span> onMeasure(<span class="keyword">int</span> widthMeasureSpec, <span class="keyword">int</span> heightMeasureSpec) { - <span class="comment">// super.onMeasure(widthMeasureSpec, heightMeasureSpec);</span> - <span class="comment">// 设置当前view的大小 width:view的宽,单位都是像素值 heigth:view的高,单位都是像素值</span> - setMeasuredDimension(backgroundBitmap.getWidth(), - backgroundBitmap.getHeight()); - } - - <span class="comment">// 这个方法对于自定义view的时候帮助不大,因为view的位置一般由父组件来决定的</span> - <span class="annotation">@Override</span> - <span class="keyword">protected</span> <span class="keyword">void</span> onLayout(<span class="keyword">boolean</span> changed, <span class="keyword">int</span> left, <span class="keyword">int</span> top, <span class="keyword">int</span> right, - <span class="keyword">int</span> bottom) { - <span class="keyword">super</span>.onLayout(changed, left, top, right, bottom); - } - - <span class="comment">/**</span> - <span class="comment"> * 画view的方法,绘制当前view的内容</span> - <span class="comment"> */</span> - <span class="annotation">@Override</span> - <span class="keyword">protected</span> <span class="keyword">void</span> onDraw(Canvas canvas) { - <span class="comment">// super.onDraw(canvas);</span> - - Paint paint = <span class="keyword">new</span> Paint(); - <span class="comment">// 打开抗锯齿</span> - paint.setAntiAlias(<span class="keyword">true</span>); - - <span class="comment">// 画背景</span> - canvas.drawBitmap(backgroundBitmap, <span class="number"></span>, <span class="number"></span>, paint); - <span class="comment">// 画滑块</span> - canvas.drawBitmap(slideButton, slideBtn_left, <span class="number"></span>, paint); - } - - <span class="comment">/**</span> - <span class="comment"> * 初始化view</span> - <span class="comment"> */</span> - <span class="keyword">private</span> <span class="keyword">void</span> initView() { - backgroundBitmap = BitmapFactory.decodeResource(getResources(), - R.drawable.switch_background); - slideButton = BitmapFactory.decodeResource(getResources(), - R.drawable.slide_button); - - <span class="comment">/*</span> - <span class="comment"> * 点击事件</span> - <span class="comment"> */</span> - setOnClickListener(<span class="keyword">new</span> OnClickListener() { - - <span class="annotation">@Override</span> - <span class="keyword">public</span> <span class="keyword">void</span> onClick(View v) { - - currentState = !currentState; - flushState(); - flushView(); - } - }); - } - - <span class="comment">/**</span> - <span class="comment"> * 刷新视图</span> - <span class="comment"> */</span> - <span class="keyword">protected</span> <span class="keyword">void</span> flushView() { - <span class="comment">// 刷新当前view会导致ondraw方法的执行</span> - invalidate(); - } - - <span class="comment">/**</span> - <span class="comment"> * 刷新当前的状态</span> - <span class="comment"> */</span> - <span class="keyword">protected</span> <span class="keyword">void</span> flushState() { - <span class="keyword">if</span> (currentState) { - slideBtn_left = backgroundBitmap.getWidth() - &#8211; slideButton.getWidth(); - } <span class="keyword">else</span> { - slideBtn_left = <span class="number"></span>; - } - } - - } </div> &nbsp; 在布局文件中将其定义出来: <div class="dp-highlighter bg_html"> <div class="bar"> <div class="tools"> **[html]** [view plain](http://blog.csdn.net/cyp331203/article/details/40736027#)<span class="tracking-ad" data-mod="popu_168"><span class="tracking-ad" data-mod="popu_168"> [copy](http://blog.csdn.net/cyp331203/article/details/40736027#)</span></span> <div> <embed id="ZeroClipboardMovie_3" src="http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf" type="application/x-shockwave-flash" width="18" height="18" align="middle" name="ZeroClipboardMovie_3"> </embed> </div> <span class="tracking-ad" data-mod="popu_167">[![在CODE上查看代码片](https://code.csdn.net/assets/CODE_ico.png)](https://code.csdn.net/snippets/505895)</span><span class="tracking-ad" data-mod="popu_170">[![派生到我的代码片](https://code.csdn.net/assets/ico_fork.svg)](https://code.csdn.net/snippets/505895/fork)</span></div> </div> - <span class="tag"><</span><span class="tag-name">RelativeLayout</span> <span class="attribute">xmlns:android</span>=<span class="attribute-value">&#8220;http://schemas.android.com/apk/res/android&#8221;</span> - <span class="attribute">xmlns:tools</span>=<span class="attribute-value">&#8220;http://schemas.android.com/tools&#8221;</span> - <span class="attribute">android:layout_width</span>=<span class="attribute-value">&#8220;match_parent&#8221;</span> - <span class="attribute">android:layout_height</span>=<span class="attribute-value">&#8220;match_parent&#8221;</span> - <span class="attribute">tools:context</span>=<span class="attribute-value">&#8220;<span class="katex math inline">{relativePackage}.</span>{activityClass}&#8221;</span> <span class="tag">></span> - - - <span class="tag"><</span><span class="tag-name">com.example.togglebutton.ui.MyToggleButton</span> - <span class="attribute">android:id</span>=<span class="attribute-value">&#8220;@+id/my_toggle_btn&#8221;</span> - <span class="attribute">android:layout_width</span>=<span class="attribute-value">&#8220;wrap_content&#8221;</span> - <span class="attribute">android:layout_height</span>=<span class="attribute-value">&#8220;wrap_content&#8221;</span> - <span class="attribute">android:layout_centerInParent</span>=<span class="attribute-value">&#8220;true&#8221;</span> <span class="tag">/></span> - - - <span class="tag"></</span><span class="tag-name">RelativeLayout</span><span class="tag">></span> </div> &nbsp; 在这里由于没有写任何点击触发业务的逻辑,只是一个单纯的控件,所以在MainActivity里面没有加入多的代码: <div class="dp-highlighter bg_java"> <div class="bar"> <div class="tools"> **[java]** [view plain](http://blog.csdn.net/cyp331203/article/details/40736027#)<span class="tracking-ad" data-mod="popu_168"><span class="tracking-ad" data-mod="popu_168"> [copy](http://blog.csdn.net/cyp331203/article/details/40736027#)</span></span> <div> <embed id="ZeroClipboardMovie_4" src="http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf" type="application/x-shockwave-flash" width="18" height="18" align="middle" name="ZeroClipboardMovie_4"> </embed> </div> <span class="tracking-ad" data-mod="popu_167">[![在CODE上查看代码片](https://code.csdn.net/assets/CODE_ico.png)](https://code.csdn.net/snippets/505895)</span><span class="tracking-ad" data-mod="popu_170">[![派生到我的代码片](https://code.csdn.net/assets/ico_fork.svg)](https://code.csdn.net/snippets/505895/fork)</span></div> </div> - <span class="keyword">package</span> com.example.togglebutton; - - <span class="keyword">import</span> android.app.Activity; - <span class="keyword">import</span> android.os.Bundle; - - <span class="keyword">public</span> <span class="keyword">class</span> MainActivity <span class="keyword">extends</span> Activity { - - <span class="annotation">@Override</span> - <span class="keyword">protected</span> <span class="keyword">void</span> onCreate(Bundle savedInstanceState) { - <span class="keyword">super</span>.onCreate(savedInstanceState); - setContentView(R.layout.activity_main); - - } - } </div> 至此一个自定义的开关按钮就完成了,后面两篇将会介绍如何在上面实现 **[点击拖动开关的效果](http://blog.csdn.net/cyp331203/article/details/40779335)** 和如何实现自定义属性,谢谢支持!

2016年4月6日 · 5 分钟 · 天边的星星

Android自定义控件系列七:详解onMeasure()方法中如何测量一个控件尺寸(一)

转载请注明出处:http://blog.csdn.net/cyp331203/article/details/45027641 自定义view/viewgroup要重写的几个方法:onMeasure(),onLayout(),onDraw()。(不熟悉的话可以查看专栏的前几篇文章:Android自定义控件系列二:自定义开关按钮(一))。 今天的任务就是详细研究一下protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)方法。 如果只是说要重写什么方法有什么用的话,还是不太清楚。先去源码中看看为什么要重写onMeasure()方法,这个方法是在哪里调用的: 一、源码中的measure/onMeasure方法: **[java]** [view plain](http://blog.csdn.net/cyp331203/article/details/45027641#) [copy](http://blog.csdn.net/cyp331203/article/details/45027641#) <div> <embed id="ZeroClipboardMovie_1" src="http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf" type="application/x-shockwave-flash" width="18" height="18" align="middle" name="ZeroClipboardMovie_1"> </embed> </div> <span class="tracking-ad" data-mod="popu_167">[![在CODE上查看代码片](https://code.csdn.net/assets/CODE_ico.png)](https://code.csdn.net/snippets/642862)</span><span class="tracking-ad" data-mod="popu_170">[![派生到我的代码片](https://code.csdn.net/assets/ico_fork.svg)](https://code.csdn.net/snippets/642862/fork)</span></div> </div> - <span class="keyword">protected</span> <span class="keyword">void</span> onMeasure(<span class="keyword">int</span> widthMeasureSpec, <span class="keyword">int</span> heightMeasureSpec) { - setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec), - getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec)); - } </div> &nbsp; 实际上是在View这个类中的public final void measure(int widthMeasureSpec, int heightMeasureSpec)方法中被调用的: <div class="dp-highlighter bg_java"> <div class="bar"> <div class="tools"> **[java]** [view plain](http://blog.csdn.net/cyp331203/article/details/45027641#)<span class="tracking-ad" data-mod="popu_168"><span class="tracking-ad" data-mod="popu_168"> [copy](http://blog.csdn.net/cyp331203/article/details/45027641#)</span></span> <div> <embed id="ZeroClipboardMovie_2" src="http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf" type="application/x-shockwave-flash" width="18" height="18" align="middle" name="ZeroClipboardMovie_2"> </embed> </div> <span class="tracking-ad" data-mod="popu_167">[![在CODE上查看代码片](https://code.csdn.net/assets/CODE_ico.png)](https://code.csdn.net/snippets/642862)</span><span class="tracking-ad" data-mod="popu_170">[![派生到我的代码片](https://code.csdn.net/assets/ico_fork.svg)](https://code.csdn.net/snippets/642862/fork)</span></div> </div> - <span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">void</span> measure(<span class="keyword">int</span> widthMeasureSpec, <span class="keyword">int</span> heightMeasureSpec) { - &#8230; - - onMeasure(widthMeasureSpec, heightMeasureSpec); - &#8230; - - } </div> &nbsp; ## <a name="t1"></a>1、measure() 可以看到,measure()这个方法是一个由final来修饰的方法,意味着不能够被子类重写.measure()方法的作用是:测量出一个View的实际大小,而实际性的测量工作,Android系统却并没有帮我们完成,因为这个工作交给了onMeasure()来作,所以我们需要在自定义View的时候按照自己的需求,重写onMeasure方法.而子控件又分为view和viewGroup两种情况,那么测量的流程是怎样的呢,看一下下面这个图你就明白了: ![](http://img.blog.csdn.net/20150413165429783) ## <a name="t2"></a> ## <a name="t3"></a>2、onMeasure onMeasure(int widthMeasureSpec, int heightMeasureSpec)中,两个参数的作用: widthMeasureSpec和heightMeasureSpec这两个int类型的参数,看名字应该知道是跟宽和高有关系,但它们其实不是宽和高,而是由宽、高和各自方向上对应的模式来合成的一个值:其中,在int类型的32位二进制位中,31-30这两位表示模式,0~29这三十位表示宽和高的实际值.其中模式一共有三种,被定义在Android中的View类的一个内部类中:View.MeasureSpec: ①UNSPECIFIED:表示默认值,父控件没有给子view任何限制。&#8212;&#8212;二进制表示:00 ②EXACTLY:表示父控件给子view一个具体的值,子view要设置成这些值的大小。&#8212;&#8212;二进制表示:01 ③AT_MOST:表示父控件个子view一个最大的特定值,而子view不能超过这个值的大小。&#8212;&#8212;二进制表示:10 # <a name="t4"></a>二、MeasureSpec MeasureSpe描述了父View对子View大小的期望.里面包含了测量模式和大小.我们可以通过以下方式从MeasureSpec中提取模式和大小,该方法内部是采用位移计算. int specMode = MeasureSpec.getMode(measureSpec);//得到模式 int specSize = MeasureSpec.getSize(measureSpec);//得到大小 也可以通过MeasureSpec的静态方法把大小和模式合成,该方法内部只是简单的相加. MeasureSpec.makeMeasureSpec(specSize,specMode); 每个View都包含一个ViewGroup.LayoutParams类或者其派生类,LayoutParams中包含了View和它的父View之间的关系,而View大小正是View和它的父View共同决定的。 我们平常使用类似于RelativeLayout和LinearLayout的时候,在其内部添加view的时候,不管是布局文件中加入还是在代码中使用addView方法添加,实际上都会调用这个onMeasure方法,而measure和onMeasure中的两个参数,是由各级父控件往子控件/子view进行一层层传递的。我们可以在xml中定义Layout的宽和高的具体的值或宽高的填充方式:matchparent/wrapcontent,也可以在代码中使用LayoutParams设置,而实际上这里设置的值就会对应到上面的measure和onMeasure方法中的两个参数的模式,对应关系如下: 具体的值(如width=200dp)和matchparent/fillparent,对应模式中的MeasureSpec.EXACTLY 包裹内容(width=wrapcontent)则对应模式中的MeasureSpec.AT_MOST 系统调用measure方法,从父控件到子控件的heightMeasureSpec的传递是有一套对应的判断规则的,列表如下: ![](http://img.blog.csdn.net/20150413170503566) 一个view的宽高尺寸,只有在测量之后才能得到,也就是measure方法被调用之后。大家都应该使用过View.getWidth()和View.getHeight()方法,这两个方法可以返回view的宽和高,但是它们也不是在一开始就可以得到的,比如oncreate方法中,因为这时候measure方法还没有被执行,测量还没有完成,我们可以来作一个简单的实验:自定义一个MyView,继承View类,然后在OnCreate方法中,将其new出来,通过addview方法,添加到现在的布局中。然后调用MyView对象的getWidth()和getHeight()方法,会发现得到的都是0。 onMeasure通过父View传递过来的大小和模式,以及自身的背景图片的大小得出自身最终的大小,然后通过setMeasuredDimension()方法设置给mMeasuredWidth和mMeasuredHeight. 普通View的onMeasure逻辑大同小异,基本都是测量自身内容和背景,然后根据父View传递过来的MeasureSpec进行最终的大小判定,例如TextView会根据文字的长度,文字的大小,文字行高,文字的行宽,显示方式,背景图片,以及父View传递过来的模式和大小最终确定自身的大小. # <a name="t5"></a>三、ViewGroup的onMeasure ViewGroup是个抽象类,本身没有实现onMeasure,但是他的子类都有各自的实现,通常他们都是通过measureChildWithMargins函数或者其他类似于measureChild的函数来遍历测量子View,被GONE的子View将不参与测量,当所有的子View都测量完毕后,才根据父View传递过来的模式和大小来最终决定自身的大小. 在测量子View时,会先获取子View的LayoutParams,从中取出宽高,如果是大于0,将会以精确的模式加上其值组合成MeasureSpec传递子View,如果是小于0,将会把自身的大小或者剩余的大小传递给子View,其模式判定在前面表中有对应关系. ViewGroup一般都在测量完所有子View后才会调用setMeasuredDimension()设置自身大小,如第一张图所示. 可能看到现在,还是没搞清楚Android系统通过measure和onmeasure一层层传递参数的具体方法。在研究这个问题之前,先来看一下最简单的helloworld的UI层级关系图: 为了方便起见,这里我们使用requestWindowFeature(Window.FEATURE_NO_TITLE);去除标题栏的影响,只看层级关系。 <div class="dp-highlighter bg_html"> <div class="bar"> <div class="tools"> **[html]** [view plain](http://blog.csdn.net/cyp331203/article/details/45027641#)<span class="tracking-ad" data-mod="popu_168"><span class="tracking-ad" data-mod="popu_168"> [copy](http://blog.csdn.net/cyp331203/article/details/45027641#)</span></span> <div> <embed id="ZeroClipboardMovie_3" src="http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf" type="application/x-shockwave-flash" width="18" height="18" align="middle" name="ZeroClipboardMovie_3"> </embed> </div> <span class="tracking-ad" data-mod="popu_167">[![在CODE上查看代码片](https://code.csdn.net/assets/CODE_ico.png)](https://code.csdn.net/snippets/642862)</span><span class="tracking-ad" data-mod="popu_170">[![派生到我的代码片](https://code.csdn.net/assets/ico_fork.svg)](https://code.csdn.net/snippets/642862/fork)</span></div> </div> - <span class="tag"><</span><span class="tag-name">RelativeLayout</span> <span class="attribute">xmlns:android</span>=<span class="attribute-value">&#8220;http://schemas.android.com/apk/res/android&#8221;</span> - <span class="attribute">xmlns:tools</span>=<span class="attribute-value">&#8220;http://schemas.android.com/tools&#8221;</span> - <span class="attribute">android:layout_width</span>=<span class="attribute-value">&#8220;match_parent&#8221;</span> - <span class="attribute">android:layout_height</span>=<span class="attribute-value">&#8220;match_parent&#8221;</span> - <span class="attribute">tools:context</span>=<span class="attribute-value">&#8220;<span class="katex math inline">{relativePackage}.</span>{activityClass}&#8221;</span> <span class="tag">></span> - - <span class="tag"><</span><span class="tag-name">TextView</span> - <span class="attribute">android:layout_width</span>=<span class="attribute-value">&#8220;wrap_content&#8221;</span> - <span class="attribute">android:layout_height</span>=<span class="attribute-value">&#8220;wrap_content&#8221;</span> - <span class="attribute">android:text</span>=<span class="attribute-value">&#8220;@string/hello_world&#8221;</span> <span class="tag">/></span> - - <span class="tag"></</span><span class="tag-name">RelativeLayout</span><span class="tag">></span> </div> &nbsp; <div class="dp-highlighter bg_java"> <div class="bar"> <div class="tools"> **[java]** [view plain](http://blog.csdn.net/cyp331203/article/details/45027641#)<span class="tracking-ad" data-mod="popu_168"><span class="tracking-ad" data-mod="popu_168"> [copy](http://blog.csdn.net/cyp331203/article/details/45027641#)</span></span> <div> <embed id="ZeroClipboardMovie_4" src="http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf" type="application/x-shockwave-flash" width="18" height="18" align="middle" name="ZeroClipboardMovie_4"> </embed> </div> <span class="tracking-ad" data-mod="popu_167">[![在CODE上查看代码片](https://code.csdn.net/assets/CODE_ico.png)](https://code.csdn.net/snippets/642862)</span><span class="tracking-ad" data-mod="popu_170">[![派生到我的代码片](https://code.csdn.net/assets/ico_fork.svg)](https://code.csdn.net/snippets/642862/fork)</span></div> </div> - <span class="keyword">package</span> com.example.hello; - - <span class="keyword">import</span> android.app.Activity; - <span class="keyword">import</span> android.os.Bundle; - <span class="keyword">import</span> android.view.Window; - - <span class="keyword">public</span> <span class="keyword">class</span> MainActivity <span class="keyword">extends</span> Activity { - - <span class="annotation">@Override</span> - <span class="keyword">protected</span> <span class="keyword">void</span> onCreate(Bundle savedInstanceState) { - <span class="keyword">super</span>.onCreate(savedInstanceState); - requestWindowFeature(Window.FEATURE_NO_TITLE); - setContentView(R.layout.activity_main); - } - } </div> UI层级关系图: ![](http://img.blog.csdn.net/20150413172415210) 可以发现最简单的helloworld的层级关系图是这样的,最开始是一个PhoneWindow的内部类DecorView,这个DecorView实际上是系统最开始加载的最底层的一个viewGroup,它是FrameLayout的子类,然后加载了一个LinearLayout,然后在这个LinearLayout上加载了一个id为content的FrameLayout和一个ViewStub,这个实际上是原本为ActionBar的位置,由于我们使用了requestWindowFeature(Window.FEATURE_NO_TITLE),于是变成了空的ViewStub;然后在id为content的FrameLayout才加载了我们的布局XML文件中写的RelativeLayout和TextView。 那么measure方法在系统中传递尺寸和模式,必定是从DecorView这一层开始的,我们假定手机屏幕是320*480,那么DecorView最开始是从硬件的配置文件中读取手机的尺寸,然后设置measure的参数大小为320*480,而模式是EXCACTLY,传递关系可以由下图示意: ![](http://img.blog.csdn.net/20150413174301575) <div> </div> &nbsp; 好了,原理将到这里,下一篇将看到利用onMeasure来测量一个自定义一个ImageView,使其能够自动填满屏幕的宽度,且能通过measure测量高度,自适应的调整高度,永远不出现拉伸/压缩变形的情况,敬请关注,谢谢。 [Android自定义控件系列八:详解onMeasure()(二)&#8211;利用onMeasure测量来实现图片拉伸永不变形,解决屏幕适配问题](http://blog.csdn.net/cyp331203/article/details/45038329) **转载请注明出处:[http://blog.csdn.net/cyp331203/article/details/45027641](http://blog.csdn.net/cyp331203/article/details/45027641)**

2016年4月6日 · 2 分钟 · 天边的星星

利用onMeasure测量来实现图片拉伸永不变形,解决屏幕适配问题

使用ImageView会遇到的问题 转载请注明出处:http://blog.csdn.net/cyp331203/article/details/45038329 在Android应用中,都少不了图片的显示,ImageView,轮播图,ViewPager等等,很多都是来显示图片的,比如一个广告条的轮播效果,参看博客:广告条效果实现—-ViewPager加载大图片(LruCache)以及定时刷新,很多时候,我们都希望图片能够在宽度上填充父窗体,这样比较符合人的审美观点,但是问题就随之而来了,那就是高度如何定义??先来看一个普通的ImageView的 Xml布局文件的定义: **[html]** [view plain](http://46aae4d1e2371e4aa769798941cef698.devproxy.yunshipei.com/cyp331203/article/details/45038329)[copy](http://46aae4d1e2371e4aa769798941cef698.devproxy.yunshipei.com/cyp331203/article/details/45038329)[![在CODE上查看代码片](https://code.csdn.net/assets/CODE_ico.png)](https://code.csdn.net/snippets/643475)[![派生到我的代码片](https://code.csdn.net/assets/ico_fork.svg)](https://code.csdn.net/snippets/643475/fork) <div> </div> <div> <embed id="ZeroClipboardMovie_1" src="http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf" type="application/x-shockwave-flash" width="18" height="18" align="middle" name="ZeroClipboardMovie_1"> </embed> </div> </div> - <span class="tag"><</span><span class="tag-name">LinearLayout</span> <span class="attribute">xmlns:android</span>=<span class="attribute-value">&#8220;http://schemas.android.com/apk/res/android&#8221;</span> - <span class="attribute">xmlns:tools</span>=<span class="attribute-value">&#8220;http://schemas.android.com/tools&#8221;</span> - <span class="attribute">android:layout_width</span>=<span class="attribute-value">&#8220;match_parent&#8221;</span> - <span class="attribute">android:layout_height</span>=<span class="attribute-value">&#8220;match_parent&#8221;</span> - <span class="attribute">android:orientation</span>=<span class="attribute-value">&#8220;vertical&#8221;</span> <span class="tag">></span> - - <span class="tag"><</span><span class="tag-name">ImageView</span> - <span class="attribute">android:layout_width</span>=<span class="attribute-value">&#8220;fill_parent&#8221;</span> - <span class="attribute">android:layout_height</span>=<span class="attribute-value">&#8220;wrap_content&#8221;</span> - <span class="attribute">android:src</span>=<span class="attribute-value">&#8220;@drawable/recommend_39&#8221;</span> <span class="tag">/></span> - - <span class="tag"><</span><span class="tag-name">TextView</span> - <span class="attribute">android:layout_width</span>=<span class="attribute-value">&#8220;wrap_content&#8221;</span> - <span class="attribute">android:layout_height</span>=<span class="attribute-value">&#8220;wrap_content&#8221;</span> - <span class="attribute">android:text</span>=<span class="attribute-value">&#8220;描述文字信息&#8230;&#8230;&#8230;&#8230;&#8230;&#8230;&#8230;&#8230;.&#8221;</span> <span class="tag">/></span> - - <span class="tag"></</span><span class="tag-name">LinearLayout</span><span class="tag">></span> 为了方便查看,我在ImageView下面又加上了一句描述的信息的TextView,这时,父控件都是填充父窗体,而ImageView则是:横向填充父窗体,纵向包裹内容;text都是包裹内容;那么来看看显示效果: ...

2016年4月6日 · 5 分钟 · 天边的星星

使用Gradle发布Android开源项目到JCenter

欢做些开源项目的朋友,相信有不少人都希望能把自己的项目发布到公共的中央仓库,如maven中央仓库,以供别人方便地集成使用。而使用了Android Studio的同学,应该也对gradle和jcenter印象深刻,不少开源库都是发布到这里的。这一篇就主要来介绍一下,如何使用Gradle发布到jcenter。 转载:http://blog.csdn.net/maosidiaoxian/article/details/43148643 注册 先到https://bintray.com注册一个账号。 配置账号 我们需要配置一下BINTRAY_USER及BINTRAY_KEY两个属性。BINTRAY_USER即你注册的账号名,BINTRAY_KEY可通过以下方法找到。 如图所示,先点击你的账号名称进入个人页面。 ![](http://img.blog.csdn.net/20150126092456203?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbWFvc2lkaWFveGlhbg==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center) 然后点击Edit,进入编辑页面。 ![](http://img.blog.csdn.net/20150126092541646?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbWFvc2lkaWFveGlhbg==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center) 接着点击API key,再点右边的Show就可以看到API KEY了。 ![](http://img.blog.csdn.net/20150126092533531?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbWFvc2lkaWFveGlhbg==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center) 然后到你的.gradle目录下(如果你没有配置过GRADLE_USER_HOME的环境变量,则是在你的用户目录下),编辑gradle.properties(如果没有则创建),加入配置: **[plain]** [view plain](http://blog.csdn.net/maosidiaoxian/article/details/43148643#)<span class="tracking-ad" data-mod="popu_168"><span class="tracking-ad" data-mod="popu_168"> [copy](http://blog.csdn.net/maosidiaoxian/article/details/43148643#)</span></span> <div> <embed id="ZeroClipboardMovie_1" src="http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf" type="application/x-shockwave-flash" width="18" height="18" align="middle" name="ZeroClipboardMovie_1"> </embed> </div> <span class="tracking-ad" data-mod="popu_167">[![在CODE上查看代码片](https://code.csdn.net/assets/CODE_ico.png)](https://code.csdn.net/snippets/589055)</span><span class="tracking-ad" data-mod="popu_170">[![派生到我的代码片](https://code.csdn.net/assets/ico_fork.svg)](https://code.csdn.net/snippets/589055/fork)</span> </div> </div> - BINTRAY_USER=xxx - BINTRAY_KEY=xxx 配置项目 下载我的这个项目:https://github.com/msdx/gradle-publish,把gradle.properties拷贝到你的项目中,并配置这些属性。 然后参考这里的build.gradle,在你的build.gradle上面加入: **[java]** [view plain](http://blog.csdn.net/maosidiaoxian/article/details/43148643#)<span class="tracking-ad" data-mod="popu_168"><span class="tracking-ad" data-mod="popu_168"> [copy](http://blog.csdn.net/maosidiaoxian/article/details/43148643#)</span></span> <div> <embed id="ZeroClipboardMovie_2" src="http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf" type="application/x-shockwave-flash" width="18" height="18" align="middle" name="ZeroClipboardMovie_2"> </embed> </div> <span class="tracking-ad" data-mod="popu_167">[![在CODE上查看代码片](https://code.csdn.net/assets/CODE_ico.png)](https://code.csdn.net/snippets/589055)</span><span class="tracking-ad" data-mod="popu_170">[![派生到我的代码片](https://code.csdn.net/assets/ico_fork.svg)](https://code.csdn.net/snippets/589055/fork)</span> </div> </div> - buildscript { - repositories { - jcenter() - } - dependencies { - classpath <span class="string">&#8216;com.jfrog.bintray.gradle:gradle-bintray-plugin:1.0&#8217;</span> - } - } 再加上一句: **[java]** [view plain](http://blog.csdn.net/maosidiaoxian/article/details/43148643#)<span class="tracking-ad" data-mod="popu_168"><span class="tracking-ad" data-mod="popu_168"> [copy](http://blog.csdn.net/maosidiaoxian/article/details/43148643#)</span></span> <div> <embed id="ZeroClipboardMovie_3" src="http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf" type="application/x-shockwave-flash" width="18" height="18" align="middle" name="ZeroClipboardMovie_3"> </embed> </div> <span class="tracking-ad" data-mod="popu_167">[![在CODE上查看代码片](https://code.csdn.net/assets/CODE_ico.png)](https://code.csdn.net/snippets/589055)</span><span class="tracking-ad" data-mod="popu_170">[![派生到我的代码片](https://code.csdn.net/assets/ico_fork.svg)](https://code.csdn.net/snippets/589055/fork)</span> </div> </div> - apply from: <span class="string">&#8216;你的bintray.gradle的相对路径&#8217;</span> 你的bintray.gradle可以参考我这个项目的bintray.gradle 来写一下。我这里的bintray.gradle是用来发布android项目的,发布的文件包括源码,文档以及AAR,如果你也是要发布这些,则可以直接使用。 执行命令进行发布 配置完成后,执行gradle bintray,即会进行编译、打包以及发布。 包含到jcenter中央库 先进入此页面:https://bintray.com/bintray/jcenter。 然后点击Include My Package,如下图: ![](http://img.blog.csdn.net/20150202091406259?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbWFvc2lkaWFveGlhbg==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center) 然后在弹出的对话框中搜索并勾上你的项目。 ![](http://img.blog.csdn.net/20150202091431609?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbWFvc2lkaWFveGlhbg==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center) 然后你可以写一下你的提交请求(貌似也可以不写?),点“Send”,接下来就看管理员审核了。 ![](http://img.blog.csdn.net/20150202091438535?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbWFvc2lkaWFveGlhbg==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center) 审核 等待管理员审核。通常都很快能通过。如果你比较心急,那么在审核通过之前,可以以maven {url http://dl.bintray.com/你的用户名/maven}的方式对你的仓库进行声明。 ## 补充说明 如果添加上这段脚本之后,在android studio中运行项目会出错,请注释apply from那一句。出错原因在于它会找不到我打包aar的那个任务,但是在命令行下是可以的。如果有更好的打包aar的方法,欢迎交流。

2016年3月16日 · 1 分钟 · 天边的星星

[Publish AAR To Maven] 使用 Gradle 发布 AAR 到 Maven 仓库

======================================================== 作者:qiujuer 博客:blog.csdn.net/qiujuer 网站:www.qiujuer.net 开源库:github.com/qiujuer/Genius-Android 转载请注明出处:http://blog.csdn.net/qiujuer/article/details/44195131 ——学之开源,用于开源;初学者的心态,与君共勉! ======================================================== 有一个好的库想与世界的伙伴分享,于是共享到GitHub上,于是有人看到了,但是要使用却发现配置非常难。但是发现别人的库只需要一行代码却能使用;于是我想知道为什么能这么简单。 在这篇文章中将带你了解如何发布你的库到远程仓库Maven中,又如何使用。 # <a name="t0"></a>准备 开始之前我们有一定的准备工作,如果准备好了那么后面将会非常顺畅。 - [注册 https://issues.sonatype.org 账户](http://blog.csdn.net/qiujuer/article/details/44195199),并创建你的 Group Id - 学会GPGTools的使用,并生成你的密钥对;见:[使用GPG对文件进行签名加密](http://blog.csdn.net/qiujuer/article/details/44173611) - 编写你的库,并准备好发布,工具:[Android-Studio](http://blog.csdn.net/qiujuer/article/details/41843095) - 添加发布Maven的Gradle文件 - 添加你的个人信息,如你的发布地址、库版本信息等 - 开发发布快照、以及正式版本,审核 # <a name="t1"></a>开始 前面的两项工作由于篇幅较大,单独开篇了,欢迎大家去看~~,我们从第三的地方开始。 ## <a name="t2"></a>编写Lib 编写库,这里我采用的是Android-Studio;至于其使用建立过程以及简单使用大家可见:[环境配置之正式版Android Studio 1.0](http://blog.csdn.net/qiujuer/article/details/41843095) 在这里我使用 [Genius-Android](https://github.com/qiujuer/Genius-Android) 开源库进行演示。 在本次项目中我们尝试发布 UI 部分,图中圈起来部分为我们需要的文件。 ## <a name="t3"></a>编写发布文件 发布文件就是其中的 **maven_push.gradle** 。由于代码较长就不贴代码了,大家可以去GitHub上查看。 代码已经发布到 BeFoot 开源项目中. 该代码运行在 Gradle 中,作用是按照配置进行打包代码文件,然后签名文件,最后发布你的文件到仓库。 在该代码中可以看见许多的方法与参数,如: ``` def isReleaseBuild() { &lt;span class="hljs-keyword">return&lt;/span> VERSION_NAME.contains(&lt;span class="hljs-string">"SNAPSHOT"&lt;/span>) == &lt;span class="hljs-keyword">false&lt;/span> } ...

2016年3月16日 · 2 分钟 · 天边的星星

使用Gradle发布项目到JCenter仓库

编辑推荐:稀土掘金,这是一个针对技术开发者的一个应用,你可以在掘金上获取最新最优质的技术干货,不仅仅是Android知识、前端、后端以至于产品和设计都有涉猎,想成为全栈工程师的朋友不要错过! 原文:使用Gradle发布项目到JCenter仓库 这篇文章介绍通过Gradle把开源项目发布到公共仓库JCenter中,方便你我他的事情,我们都是很懒的嘛。JCenter现在是Android Studio中repositories的默认节点了,之前是Maven的,不过JCenter是兼容Maven的,所以放心使用。步骤基本是按Publishing Gradle Android Library to jCenter Repository这里来的,英文能看的直接看这篇也行。下面我的步骤正式开始,发布到JCenter仓库的是我的项目:BounceProgressBar。 申请Bintray账号 Bintray的基本功能类似于Maven Central,一样的我们需要一个账号,Bintray传送门,注册完成后第一步算完成了。 生成项目的JavaDoc和source JARs 简单的说生成的这两样东西就是我们在下一步中上传到远程仓库JCenter上的文件了。这一步需要android-maven-plugin插件,所以我们需要在项目的build.gradle(Top-level build file,项目最外层的build.gradle文件)中添加这个构建依赖,如下: 1 <div class="line number2 index1 alt1"> 2 </div> <div class="line number3 index2 alt2"> 3 </div> <div class="line number4 index3 alt1"> 4 </div> <div class="line number5 index4 alt2"> 5 </div> <div class="line number6 index5 alt1"> 6 </div> <div class="line number7 index6 alt2"> 7 </div> <div class="line number8 index7 alt1"> 8 </div> <div class="line number9 index8 alt2"> 9 </div> <div class="line number10 index9 alt1"> 10 </div> <div class="line number11 index10 alt2"> 11 </div> <div class="line number12 index11 alt1"> 12 </div> <div class="line number13 index12 alt2"> 13 </div> <div class="line number14 index13 alt1"> 14 </div> <div class="line number15 index14 alt2"> 15 </div> <div class="line number16 index15 alt1"> 16 </div> </td> <td class="code"> <div class="container"> <div class="line number1 index0 alt2"> `buildscript {` </div> <div class="line number2 index1 alt1"> ` ``repositories {` </div> <div class="line number3 index2 alt2"> ` ``jcenter()` </div> <div class="line number4 index3 alt1"> ` ``}` </div> <div class="line number5 index4 alt2"> ` ``dependencies {` </div> <div class="line number6 index5 alt1"> ` ``classpath ``'com.android.tools.build:gradle:1.0.0'` </div> <div class="line number7 index6 alt2"> ` ``classpath ``'com.github.dcendents:android-maven-plugin:1.2'` </div> <div class="line number8 index7 alt1"> ` ``// NOTE: Do not place your application dependencies here; they belong` </div> <div class="line number9 index8 alt2"> ` ``// in the individual module build.gradle files` </div> <div class="line number10 index9 alt1"> ` ``}` </div> <div class="line number11 index10 alt2"> `}` </div> <div class="line number12 index11 alt1"> `allprojects {` </div> <div class="line number13 index12 alt2"> ` ``repositories {` </div> <div class="line number14 index13 alt1"> ` ``jcenter()` </div> <div class="line number15 index14 alt2"> ` ``}` </div> <div class="line number16 index15 alt1"> `}` </div> </div> </td> </tr> </table> 然后在你需要发布的那个module(我这里的即是library)的build.gradle里配置如下内容: ...

2016年3月16日 · 11 分钟 · 天边的星星