Android动画机制全解析

导论 本文着重讲解Android3.0后推出的属性动画框架Property Animation——Animator。 产生原因 3.0之前已有的动画框架——Animation存在一些局限性, Animation框架定义了透明度,旋转,缩放和位移几种常见的动画,而且控制的是整个View,实现原理是每次绘制视图时View所在的ViewGroup中的drawChild函数获取该View的Animation的Transformation值,然后调用canvas.concat(transformToApply.getMatrix()),通过矩阵运算完成动画帧,如果动画没有完成,继续调用invalidate()函数,启动下次绘制来驱动动画,动画过程中的帧之间间隙时间是绘制函数所消耗的时间,可能会导致动画消耗比较多的CPU资源,最重要的是,动画改变的只是显示,并不能相应事件。 而在Animator框架中使用最多的是AnimatorSet和ObjectAnimator配合,使用ObjectAnimator进行更精细化控制,只控制一个对象的一个属性值,多个ObjectAnimator组合到AnimatorSet形成一个动画。而且ObjectAnimator能够自动驱动,可以调用setFrameDelay(longframeDelay)设置动画帧之间的间隙时间,调整帧率,减少动画过程中频繁绘制界面,而在不影响动画效果的前提下减少CPU资源消耗。因此,Anroid推出的强大的属性动画框架,基本可以实现所有的动画效果。 强大的原因 因为属性动画框架操作的是真实的属性值,直接变化了对象的属性,因此可以很灵活的实现各种效果,而不局限于以前的4种动画效果。 ObjectAnimator ObjectAnimator是属性动画框架中最重要的实行类,创建一个ObjectAnimator只需通过他的静态工厂类直接返回一个ObjectAnimator对象。传的参数包括一个对象和对象的属性名字,但这个属性必须有get和set函数,内部会通过java反射机制来调用set函数修改对象属性值。还包括属性的初始值,最终值,还可以调用setInterpolator设置曲线函数。 ObjectAnimator实例 [view plain](http://www.netfoucs.com/article/x359981514/92198.html#)[copy to clipboard](http://www.netfoucs.com/article/x359981514/92198.html#)[print](http://www.netfoucs.com/article/x359981514/92198.html#)[?](http://www.netfoucs.com/article/x359981514/92198.html#) - ObjectAnimator.ofFloat(view, <span class="string">&#8220;rotationX&#8221;</span>, <span class="number"></span>.0F, <span class="number">360</span>.0F).setDuration(<span class="number">1000</span>).start(); 这个例子很简单,针对view的属性rotationX进行持续时间为1000ms的0到360的角度变换。 PS:可操纵的属性参数:x/y;scaleX/scaleY;rotationX/ rotationY;transitionX/ transitionY等等。 PS:X是View最终的位置、translationX为最终位置与布局时初始位置的差。所以若就用translationX即为在原来基础上移动多少,X为最终多少。getX()的值为getLeft()与getTranslationX()的和。 动画绘制过程的监听 [view plain](http://www.netfoucs.com/article/x359981514/92198.html#)[copy to clipboard](http://www.netfoucs.com/article/x359981514/92198.html#)[print](http://www.netfoucs.com/article/x359981514/92198.html#)[?](http://www.netfoucs.com/article/x359981514/92198.html#) - animator.addUpdateListener(<span class="keyword">new</span> AnimatorUpdateListener() { <span class="annotation">@Override</span> <span class="keyword">public</span> <span class="keyword">void</span> onAnimationUpdate(ValueAnimator arg0) { } }); 该方法用来监听动画绘制过程中的每一帧的改变,通过这个方法,我们可以在动画重绘的过程中,实现自己的逻辑。 同时修改多个属性值 当然这个可以使用Animationset来实现,这里我们使用一种取巧的方法来实现: [view plain](http://www.netfoucs.com/article/x359981514/92198.html#)[copy to clipboard](http://www.netfoucs.com/article/x359981514/92198.html#)[print](http://www.netfoucs.com/article/x359981514/92198.html#)[?](http://www.netfoucs.com/article/x359981514/92198.html#) - ObjectAnimator anim = ObjectAnimator.ofFloat(view, <span class="string">&#8220;xxx&#8221;</span>, <span class="number">1</span>.0F, <span class="number"></span>.0F) .setDuration(<span class="number">500</span>); anim.start(); anim.addUpdateListener(<span class="keyword">new</span> AnimatorUpdateListener() { <span class="annotation">@Override</span> <span class="keyword">public</span> <span class="keyword">void</span> onAnimationUpdate(ValueAnimator animation) { floatcVal = (Float) animation.getAnimatedValue(); view.setAlpha(cVal); view.setScaleX(cVal); view.setScaleY(cVal); } }); 我们可以监听一个并不存在的属性,而在监听动画更新的方法中,去修改view的属性,监听一个不存在的属性的原因就是,我们只需要动画的变化值,通过这个值,我们自己来实现要修改的效果,实际上,更直接的方法,就是使用ValueAnimator来实现,其实ObjectAnimator就是ValueAnimator的子类,这个在下面会具体讲到。 ...

2015年6月11日 · 8 分钟 · 天边的星星

Material Design开发利器

Android 5.0 Lollipop 是迄今为止最重大的一次发布,很大程度上是因为 material design —— 这是一门新的设计语言,它刷新了整个 Android 的用户体验。但是对于开发者来说,要设计出完全符合 material design 哲学的应用,是一个很大的挑战。Android Design Support Library 对此提供了很好的支持,里面汇集了很多重要的 material design 控件,支持所有 Android 2.1 及后续版本。里面你可以看到 navigation drawer view、floating labels、floating action button、snackbar、tabs,以及一套将它们紧密结合在一起的动作与滚动框架。 Navigation View(导航视图) 无论从应用标识、内容导航,还是设计一致性来讲,navigation drawer 都是首当其冲的焦点。现在,NavigationView 让导航栏变得更简单,它提供了 navigation drawer 需要的框架,以及通过资源文件来自定义更多菜单项的能力。 ![navigationview](http://ac-lhzo7z96.clouddn.com/1433285108918) 你只需要将 NavigationView 作为 DrawerLayout 的内容视图来使用即可,例如: ![drawerlayout](http://ac-lhzo7z96.clouddn.com/1433297055793) 这里你会注意到两个属性:app:heanderLaytout 用来控制 header 部分的布局;app:menu 指定了菜单资源。NavigationView 自动处理了状态栏的变化,保证可以在 API 21+ 的设备上正确运行。 最简单的 drawer 菜单就是一个允许选择的菜单项集合,例如: ![simplemenu](http://ac-lhzo7z96.clouddn.com/1433297095098) 选中的菜单会高亮显示,以提醒用户当前选择的是哪个菜单项。 你也可以在菜单中使用 subheader 来实现独立的分组: ![subheader_menu](http://ac-lhzo7z96.clouddn.com/1433297134486) 调用 setNavigationItemSelectedListener() 后,在菜单项被选中的时候,你会通过OnNavigationItemSelectedListener 得到回调。在处理回调时,你会知道是哪个菜单项被点击,此时你可以处理选择事件,修改选中状态,加载新的内容,以及通过代码来关闭 drawer,或者其他任何你想执行的操作。 文字输入时的悬浮标签 尽管 EditText 已经为 material design 做了一些改善,但是还不够,譬如它在我们输入第一个字符的时候,就会自动隐藏掉提示标签。现在你该使用 TextInputLayout 了,它会在用户开始输入之后,自动将提示标签悬浮到 EditText 上方,这样用户就永远都能知道输入内容的上下文。 ...

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

Android应用层View绘制流程与源码分析

1 背景 还记得前面《Android应用setContentView与LayoutInflater加载解析机制源码分析》这篇文章吗?我们有分析到Activity中界面加载显示的基本流程原理,记不记得最终分析结果就是下面的关系: 看见没有,如上图中id为content的内容就是整个View树的结构,所以对每个具体View对象的操作,其实就是个递归的实现。 前面《Android触摸屏事件派发机制详解与源码分析一(View篇)》文章的3-1小节说过Android中的任何一个布局、任何一个控件其实都是直接或间接继承自View实现的,当然也包括我们后面一步一步引出的自定义控件也不例外,所以说这些View应该都具有相同的绘制流程与机制才能显示到屏幕上(因为他们都具备相同的父类View,可能每个控件的具体绘制逻辑有差异,但是主流程都是一样的)。经过总结发现每一个View的绘制过程都必须经历三个最主要的过程,也就是measure、layout和draw。 既然一个View的绘制主要流程是这三步,那一定有一个开始地方呀,就像一个类从main函数执行一样呀。对于View的绘制开始调运地方这里先给出结论,本文后面会反过来分析原因的,先往下看就行。具体结论如下: 整个View树的绘图流程是在ViewRootImpl类的performTraversals()方法(这个方法巨长)开始的,该函数做的执行过程主要是根据之前设置的状态,判断是否重新计算视图大小(measure)、是否重新放置视图的位置(layout)、以及是否重绘 (draw),其核心也就是通过判断来选择顺序执行这三个方法中的哪个,如下: `&lt;span class="hljs-keyword">private&lt;/span> &lt;span class="hljs-keyword">void&lt;/span> &lt;span class="hljs-title">performTraversals&lt;/span>() { ...... &lt;span class="hljs-comment">//最外层的根视图的widthMeasureSpec和heightMeasureSpec由来&lt;/span> &lt;span class="hljs-comment">//lp.width和lp.height在创建ViewGroup实例时等于MATCH_PARENT&lt;/span> &lt;span class="hljs-keyword">int&lt;/span> childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width); &lt;span class="hljs-keyword">int&lt;/span> childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height); ...... mView.measure(childWidthMeasureSpec, childHeightMeasureSpec); ...... mView.layout(&lt;span class="hljs-number">0&lt;/span>, &lt;span class="hljs-number">0&lt;/span>, mView.getMeasuredWidth(), mView.getMeasuredHeight()); ...... mView.draw(canvas); ...... }` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 ` &lt;span class="hljs-javadoc">/** * Figures out the measure spec for the root view in a window based on it's * layout params. * *&lt;span class="hljs-javadoctag"> @param&lt;/span> windowSize * The available width or height of the window * *&lt;span class="hljs-javadoctag"> @param&lt;/span> rootDimension * The layout params for one dimension (width or height) of the * window. * *&lt;span class="hljs-javadoctag"> @return&lt;/span> The measure spec to use to measure the root view. */&lt;/span> &lt;span class="hljs-keyword">private&lt;/span> &lt;span class="hljs-keyword">static&lt;/span> &lt;span class="hljs-keyword">int&lt;/span> &lt;span class="hljs-title">getRootMeasureSpec&lt;/span>(&lt;span class="hljs-keyword">int&lt;/span> windowSize, &lt;span class="hljs-keyword">int&lt;/span> rootDimension) { &lt;span class="hljs-keyword">int&lt;/span> measureSpec; &lt;span class="hljs-keyword">switch&lt;/span> (rootDimension) { &lt;span class="hljs-keyword">case&lt;/span> ViewGroup.LayoutParams.MATCH_PARENT: &lt;span class="hljs-comment">// Window can't resize. Force root view to be windowSize.&lt;/span> measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY); &lt;span class="hljs-keyword">break&lt;/span>; ...... } &lt;span class="hljs-keyword">return&lt;/span> measureSpec; }` 1 ...

2015年6月6日 · 19 分钟 · 天边的星星

Android自定义控件(状态提示图表)

1 背景 前面分析那么多系统源码了,也该暂停下来休息一下,趁昨晚闲着看见一个有意思的需求就操练一下分析源码后的实例演练—-自定义控件。 这个实例很适合新手入门自定义控件。先看下效果图: 横屏模式如下: 竖屏模式如下: 看见没有,这个控件完全自定义的,连文字等都是自定义的,没有任何图片等资源,就仅仅是一个小的java文件,这个界面只有一个控件。如下咱们看下实现代码。 !!!!!!! 下载Demo工程源码点击我 【工匠若水 http://blog.csdn.net/yanbober 转载烦请注明出处,尊重分享成果】 2 实例代码 如下就是整个工程的源码了。 自定义上面展示的控件AreaChartsView源码: `&lt;span class="hljs-javadoc">/** * Author : yanbo * Date : 2015-06-03 * Time : 09:22 * Description : 自定义区域描述图表View */&lt;/span> &lt;span class="hljs-keyword">public&lt;/span> &lt;span class="hljs-class">&lt;span class="hljs-keyword">class&lt;/span> &lt;span class="hljs-title">AreaChartsView&lt;/span> &lt;span class="hljs-keyword">extends&lt;/span> &lt;span class="hljs-title">View&lt;/span> {&lt;/span> &lt;span class="hljs-keyword">private&lt;/span> Paint mPaint; &lt;span class="hljs-keyword">private&lt;/span> &lt;span class="hljs-keyword">int&lt;/span>[] mZeroPos = &lt;span class="hljs-keyword">new&lt;/span> &lt;span class="hljs-keyword">int&lt;/span>[&lt;span class="hljs-number">2&lt;/span>]; &lt;span class="hljs-keyword">private&lt;/span> &lt;span class="hljs-keyword">int&lt;/span>[] mMaxYPos = &lt;span class="hljs-keyword">new&lt;/span> &lt;span class="hljs-keyword">int&lt;/span>[&lt;span class="hljs-number">2&lt;/span>]; &lt;span class="hljs-keyword">private&lt;/span> &lt;span class="hljs-keyword">int&lt;/span>[] mMaxXPos = &lt;span class="hljs-keyword">new&lt;/span> &lt;span class="hljs-keyword">int&lt;/span>[&lt;span class="hljs-number">2&lt;/span>]; &lt;span class="hljs-keyword">private&lt;/span> &lt;span class="hljs-keyword">int&lt;/span> mWidth, mHight; &lt;span class="hljs-keyword">private&lt;/span> &lt;span class="hljs-keyword">int&lt;/span> mRealWidth, mRealHight; &lt;span class="hljs-keyword">private&lt;/span> String mTitleY, mTitleX; &lt;span class="hljs-keyword">private&lt;/span> ArrayList&lt;Integer&gt; mXLevel = &lt;span class="hljs-keyword">new&lt;/span> ArrayList&lt;&gt;(); &lt;span class="hljs-keyword">private&lt;/span> ArrayList&lt;Integer&gt; mYLevel = &lt;span class="hljs-keyword">new&lt;/span> ArrayList&lt;&gt;(); &lt;span class="hljs-keyword">private&lt;/span> ArrayList&lt;String&gt; mGridLevelText = &lt;span class="hljs-keyword">new&lt;/span> ArrayList&lt;&gt;(); &lt;span class="hljs-keyword">private&lt;/span> ArrayList&lt;Integer&gt; mGridColorLevel = &lt;span class="hljs-keyword">new&lt;/span> ArrayList&lt;&gt;(); &lt;span class="hljs-keyword">private&lt;/span> ArrayList&lt;Integer&gt; mGridTxtColorLevel = &lt;span class="hljs-keyword">new&lt;/span> ArrayList&lt;&gt;(); &lt;span class="hljs-keyword">private&lt;/span> &lt;span class="hljs-keyword">int&lt;/span> mGridLevel = mXLevel.size() - &lt;span class="hljs-number">1&lt;/span>; &lt;span class="hljs-comment">//title字符大小&lt;/span> &lt;span class="hljs-keyword">private&lt;/span> &lt;span class="hljs-keyword">int&lt;/span> mXYTitleTextSize = &lt;span class="hljs-number">40&lt;/span>; &lt;span class="hljs-keyword">private&lt;/span> &lt;span class="hljs-keyword">int&lt;/span> mMeasureXpos, mMeasureYpos; &lt;span class="hljs-keyword">public&lt;/span> &lt;span class="hljs-title">AreaChartsView&lt;/span>(Context context, AttributeSet attrs) { &lt;span class="hljs-keyword">super&lt;/span>(context, attrs); mPaint = &lt;span class="hljs-keyword">new&lt;/span> Paint(Paint.ANTI_ALIAS_FLAG); mPaint.setAntiAlias(&lt;span class="hljs-keyword">true&lt;/span>); mPaint.setFilterBitmap(&lt;span class="hljs-keyword">true&lt;/span>); } &lt;span class="hljs-annotation">@Override&lt;/span> &lt;span class="hljs-keyword">protected&lt;/span> &lt;span class="hljs-keyword">void&lt;/span> &lt;span class="hljs-title">onLayout&lt;/span>(&lt;span class="hljs-keyword">boolean&lt;/span> changed, &lt;span class="hljs-keyword">int&lt;/span> left, &lt;span class="hljs-keyword">int&lt;/span> top, &lt;span class="hljs-keyword">int&lt;/span> right, &lt;span class="hljs-keyword">int&lt;/span> bottom) { &lt;span class="hljs-keyword">super&lt;/span>.onLayout(changed, left, top, right, bottom); mWidth = getWidth(); mHight = getHeight(); } &lt;span class="hljs-annotation">@Override&lt;/span> &lt;span class="hljs-keyword">protected&lt;/span> &lt;span class="hljs-keyword">void&lt;/span> &lt;span class="hljs-title">onDraw&lt;/span>(Canvas canvas) { &lt;span class="hljs-keyword">super&lt;/span>.onDraw(canvas); initPosition(); drawXYTitle(canvas); drawXYLine(canvas); drawContent(canvas); } &lt;span class="hljs-keyword">private&lt;/span> &lt;span class="hljs-keyword">void&lt;/span> &lt;span class="hljs-title">initPosition&lt;/span>() { &lt;span class="hljs-comment">//初始化坐标图的xy交点原点坐标&lt;/span> mZeroPos[&lt;span class="hljs-number">0&lt;/span>] = mXYTitleTextSize * &lt;span class="hljs-number">2&lt;/span>; mZeroPos[&lt;span class="hljs-number">1&lt;/span>] = mHight - mXYTitleTextSize * &lt;span class="hljs-number">4&lt;/span>; &lt;span class="hljs-comment">//初始化坐标图的X轴最大值坐标&lt;/span> mMaxXPos[&lt;span class="hljs-number">0&lt;/span>] = mWidth; mMaxXPos[&lt;span class="hljs-number">1&lt;/span>] = mHight - mXYTitleTextSize * &lt;span class="hljs-number">4&lt;/span>; &lt;span class="hljs-comment">//初始化坐标图的Y轴最大值坐标&lt;/span> mMaxYPos[&lt;span class="hljs-number">0&lt;/span>] = mXYTitleTextSize * &lt;span class="hljs-number">2&lt;/span>; mMaxYPos[&lt;span class="hljs-number">1&lt;/span>] = mXYTitleTextSize * &lt;span class="hljs-number">2&lt;/span>; } &lt;span class="hljs-keyword">private&lt;/span> &lt;span class="hljs-keyword">void&lt;/span> &lt;span class="hljs-title">drawXYTitle&lt;/span>(Canvas canvas) { mPaint.setColor(Color.parseColor(&lt;span class="hljs-string">"#1FB0E7"&lt;/span>)); mPaint.setTextSize(mXYTitleTextSize); mPaint.setTextAlign(Paint.Align.LEFT); &lt;span class="hljs-comment">//画Y轴顶的title&lt;/span> canvas.drawText(mTitleY, mMaxYPos[&lt;span class="hljs-number">0&lt;/span>] - mXYTitleTextSize * &lt;span class="hljs-number">2&lt;/span>, mMaxYPos[&lt;span class="hljs-number">1&lt;/span>] - mXYTitleTextSize, mPaint); mPaint.setTextAlign(Paint.Align.RIGHT); &lt;span class="hljs-comment">//画X轴顶的title&lt;/span> canvas.drawText(mTitleX, mMaxXPos[&lt;span class="hljs-number">0&lt;/span>], mMaxXPos[&lt;span class="hljs-number">1&lt;/span>] + mXYTitleTextSize * &lt;span class="hljs-number">2&lt;/span>, mPaint); } &lt;span class="hljs-keyword">private&lt;/span> &lt;span class="hljs-keyword">void&lt;/span> &lt;span class="hljs-title">drawXYLine&lt;/span>(Canvas canvas) { mPaint.setColor(Color.DKGRAY); mPaint.setTextAlign(Paint.Align.RIGHT); &lt;span class="hljs-comment">//画XY轴&lt;/span> canvas.drawLine(mMaxYPos[&lt;span class="hljs-number">0&lt;/span>], mMaxYPos[&lt;span class="hljs-number">1&lt;/span>], mZeroPos[&lt;span class="hljs-number">0&lt;/span>], mZeroPos[&lt;span class="hljs-number">1&lt;/span>], mPaint); canvas.drawLine(mZeroPos[&lt;span class="hljs-number">0&lt;/span>], mZeroPos[&lt;span class="hljs-number">1&lt;/span>], mMaxXPos[&lt;span class="hljs-number">0&lt;/span>], mMaxXPos[&lt;span class="hljs-number">1&lt;/span>], mPaint); } &lt;span class="hljs-keyword">private&lt;/span> &lt;span class="hljs-keyword">void&lt;/span> &lt;span class="hljs-title">drawContent&lt;/span>(Canvas canvas) { mGridLevel = mXLevel.size() - &lt;span class="hljs-number">1&lt;/span>; &lt;span class="hljs-comment">//计算出偏移title等显示尺标后的真实XY轴长度,便于接下来等分&lt;/span> mRealWidth = (mWidth - mXYTitleTextSize * &lt;span class="hljs-number">2&lt;/span>); mRealHight = (mHight - mXYTitleTextSize * &lt;span class="hljs-number">4&lt;/span>); &lt;span class="hljs-comment">//算出等分间距&lt;/span> &lt;span class="hljs-keyword">int&lt;/span> offsetX = mRealWidth/(mGridLevel); &lt;span class="hljs-keyword">int&lt;/span> offsetY = mRealHight/(mGridLevel+&lt;span class="hljs-number">1&lt;/span>); &lt;span class="hljs-comment">//循环绘制content&lt;/span> &lt;span class="hljs-keyword">for&lt;/span> (&lt;span class="hljs-keyword">int&lt;/span> index=&lt;span class="hljs-number">0&lt;/span>; index&lt;mGridLevel+&lt;span class="hljs-number">1&lt;/span>; index++) { mPaint.setColor(Color.DKGRAY); mPaint.setTextAlign(Paint.Align.RIGHT); mPaint.setTextSize(mXYTitleTextSize-&lt;span class="hljs-number">5&lt;/span>); &lt;span class="hljs-comment">//绘制X轴的那些坐标区间点,包含0点坐标&lt;/span> canvas.drawText(String.valueOf(mXLevel.get(index)), mZeroPos[&lt;span class="hljs-number">0&lt;/span>]+(index*offsetX), mZeroPos[&lt;span class="hljs-number">1&lt;/span>] + mXYTitleTextSize, mPaint); &lt;span class="hljs-keyword">if&lt;/span> (index != &lt;span class="hljs-number">0&lt;/span>) { &lt;span class="hljs-comment">//绘制Y轴坐标区间点,不包含0点坐标,X轴已经画过了&lt;/span> canvas.drawText(String.valueOf(mYLevel.get(index)), mZeroPos[&lt;span class="hljs-number">0&lt;/span>], mZeroPos[&lt;span class="hljs-number">1&lt;/span>]-(index*offsetY), mPaint); } &lt;span class="hljs-keyword">if&lt;/span> (index == mGridLevel) { &lt;span class="hljs-comment">//坐标区间 = 真实区间 + 1&lt;/span> &lt;span class="hljs-keyword">break&lt;/span>; } mPaint.setColor(mGridColorLevel.get(mGridLevel - &lt;span class="hljs-number">1&lt;/span> - index)); mPaint.setStyle(Paint.Style.FILL); &lt;span class="hljs-comment">//绘制区间叠加图谱方块,从远到0坐标,因为小的图会覆盖大的图&lt;/span> canvas.drawRect(mMaxYPos[&lt;span class="hljs-number">0&lt;/span>], mMaxYPos[&lt;span class="hljs-number">1&lt;/span>] + index*offsetY, mMaxXPos[&lt;span class="hljs-number">0&lt;/span>]-index*offsetX, mMaxXPos[&lt;span class="hljs-number">1&lt;/span>], mPaint); mPaint.setColor(mGridTxtColorLevel.get(index)); mPaint.setTextAlign(Paint.Align.RIGHT); mPaint.setTextSize(mXYTitleTextSize); &lt;span class="hljs-comment">//绘制每个方块状态区间的提示文字&lt;/span> canvas.drawText(mGridLevelText.get(index), mMaxXPos[&lt;span class="hljs-number">0&lt;/span>] - index * offsetX - mXYTitleTextSize, mMaxYPos[&lt;span class="hljs-number">1&lt;/span>] + index * offsetY + mXYTitleTextSize, mPaint); } &lt;span class="hljs-comment">//绘制当前坐标&lt;/span> drawNotice(canvas, offsetX, offsetY); } &lt;span class="hljs-keyword">private&lt;/span> &lt;span class="hljs-keyword">void&lt;/span> &lt;span class="hljs-title">drawNotice&lt;/span>(Canvas canvas, &lt;span class="hljs-keyword">int&lt;/span> offsetX, &lt;span class="hljs-keyword">int&lt;/span> offsetY) { &lt;span class="hljs-keyword">int&lt;/span> realPosX = &lt;span class="hljs-number">0&lt;/span>; &lt;span class="hljs-keyword">int&lt;/span> realPosY = &lt;span class="hljs-number">0&lt;/span>; &lt;span class="hljs-comment">//计算传入的x值与真实屏幕坐标的像素值的百分比差值转换&lt;/span> &lt;span class="hljs-keyword">for&lt;/span> (&lt;span class="hljs-keyword">int&lt;/span> index=&lt;span class="hljs-number">0&lt;/span>; index&lt;mGridLevel; index++) { &lt;span class="hljs-keyword">if&lt;/span> (mMeasureXpos &gt;= mXLevel.get(index) && mMeasureXpos &lt; mXLevel.get(index+&lt;span class="hljs-number">1&lt;/span>)) { &lt;span class="hljs-keyword">int&lt;/span> subValue = mMeasureXpos - mXLevel.get(index); &lt;span class="hljs-keyword">int&lt;/span> offset = mXLevel.get(index+&lt;span class="hljs-number">1&lt;/span>) - mXLevel.get(index); realPosX = mZeroPos[&lt;span class="hljs-number">0&lt;/span>] + index*offsetX + (subValue / offset); &lt;span class="hljs-keyword">break&lt;/span>; } } &lt;span class="hljs-comment">//计算传入的y值与真实屏幕坐标的像素值的百分比差值转换&lt;/span> &lt;span class="hljs-keyword">for&lt;/span> (&lt;span class="hljs-keyword">int&lt;/span> index=&lt;span class="hljs-number">0&lt;/span>; index&lt;mGridLevel; index++) { &lt;span class="hljs-keyword">if&lt;/span> (mMeasureYpos &gt;= mYLevel.get(index) && mMeasureYpos &lt; mYLevel.get(index+&lt;span class="hljs-number">1&lt;/span>)) { &lt;span class="hljs-keyword">int&lt;/span> subValue = mMeasureYpos - mYLevel.get(index); &lt;span class="hljs-keyword">int&lt;/span> offset = mYLevel.get(index+&lt;span class="hljs-number">1&lt;/span>) - mYLevel.get(index); realPosY = mZeroPos[&lt;span class="hljs-number">1&lt;/span>] - index*offsetY - (offsetY - (subValue / offset)); &lt;span class="hljs-keyword">break&lt;/span>; } } &lt;span class="hljs-comment">//画我们传入的坐标点的标记小红点&lt;/span> mPaint.setColor(Color.RED); mPaint.setStyle(Paint.Style.FILL); canvas.drawCircle(realPosX, realPosY, &lt;span class="hljs-number">8&lt;/span>, mPaint); &lt;span class="hljs-keyword">int&lt;/span>[] centerPos = {mZeroPos[&lt;span class="hljs-number">0&lt;/span>] + mRealWidth/&lt;span class="hljs-number">2&lt;/span>, mZeroPos[&lt;span class="hljs-number">1&lt;/span>] - mRealHight/&lt;span class="hljs-number">2&lt;/span>}; mPaint.setColor(Color.WHITE); mPaint.setStyle(Paint.Style.FILL_AND_STROKE); RectF rectF = &lt;span class="hljs-keyword">null&lt;/span>; Path path = &lt;span class="hljs-keyword">new&lt;/span> Path(); &lt;span class="hljs-comment">//画红点旁边的提示框和文字,有四个区域,然后提示框的小三角指标方位不同&lt;/span> &lt;span class="hljs-keyword">if&lt;/span> (realPosX &lt;= centerPos[&lt;span class="hljs-number">0&lt;/span>] && realPosY &gt;= centerPos[&lt;span class="hljs-number">1&lt;/span>]) { &lt;span class="hljs-comment">//left-bottom&lt;/span> &lt;span class="hljs-comment">//画三角形&lt;/span> path.moveTo(realPosX+&lt;span class="hljs-number">5&lt;/span>, realPosY+&lt;span class="hljs-number">5&lt;/span>); path.lineTo(realPosX+&lt;span class="hljs-number">15&lt;/span>, realPosY+&lt;span class="hljs-number">15&lt;/span>); path.lineTo(realPosX+&lt;span class="hljs-number">15&lt;/span>, realPosY-&lt;span class="hljs-number">15&lt;/span>); &lt;span class="hljs-comment">//画矩形背景&lt;/span> rectF = &lt;span class="hljs-keyword">new&lt;/span> RectF(realPosX+&lt;span class="hljs-number">15&lt;/span>, realPosY-&lt;span class="hljs-number">40&lt;/span>, realPosX+&lt;span class="hljs-number">200&lt;/span>, realPosY + &lt;span class="hljs-number">30&lt;/span>); canvas.drawRoundRect(rectF, &lt;span class="hljs-number">15&lt;/span>, &lt;span class="hljs-number">15&lt;/span>, mPaint); &lt;span class="hljs-comment">//画提示框的文字&lt;/span> mPaint.reset(); mPaint.setColor(Color.RED); mPaint.setTextSize(mXYTitleTextSize - &lt;span class="hljs-number">5&lt;/span>); canvas.drawText(&lt;span class="hljs-string">"("&lt;/span>+mMeasureXpos+&lt;span class="hljs-string">", "&lt;/span>+mMeasureYpos+&lt;span class="hljs-string">")"&lt;/span>, realPosX+&lt;span class="hljs-number">30&lt;/span>, realPosY, mPaint); } &lt;span class="hljs-keyword">else&lt;/span> &lt;span class="hljs-keyword">if&lt;/span> (realPosX &lt;= centerPos[&lt;span class="hljs-number">0&lt;/span>] && realPosY &lt; centerPos[&lt;span class="hljs-number">1&lt;/span>]) { &lt;span class="hljs-comment">//left-top&lt;/span> path.moveTo(realPosX+&lt;span class="hljs-number">5&lt;/span>, realPosY+&lt;span class="hljs-number">5&lt;/span>); path.lineTo(realPosX+&lt;span class="hljs-number">15&lt;/span>, realPosY+&lt;span class="hljs-number">15&lt;/span>); path.lineTo(realPosX + &lt;span class="hljs-number">15&lt;/span>, realPosY - &lt;span class="hljs-number">15&lt;/span>); rectF = &lt;span class="hljs-keyword">new&lt;/span> RectF(realPosX+&lt;span class="hljs-number">15&lt;/span>, realPosY - &lt;span class="hljs-number">20&lt;/span>, realPosX+&lt;span class="hljs-number">200&lt;/span>, realPosY + &lt;span class="hljs-number">50&lt;/span>); canvas.drawRoundRect(rectF, &lt;span class="hljs-number">15&lt;/span>, &lt;span class="hljs-number">15&lt;/span>, mPaint); mPaint.reset(); mPaint.setColor(Color.RED); mPaint.setTextSize(mXYTitleTextSize - &lt;span class="hljs-number">5&lt;/span>); canvas.drawText(&lt;span class="hljs-string">"("&lt;/span>+mMeasureXpos+&lt;span class="hljs-string">", "&lt;/span>+mMeasureYpos+&lt;span class="hljs-string">")"&lt;/span>, realPosX+&lt;span class="hljs-number">30&lt;/span>, realPosY+&lt;span class="hljs-number">20&lt;/span>, mPaint); } &lt;span class="hljs-keyword">else&lt;/span> &lt;span class="hljs-keyword">if&lt;/span> (realPosX &gt; centerPos[&lt;span class="hljs-number">0&lt;/span>] && realPosY &gt;= centerPos[&lt;span class="hljs-number">1&lt;/span>]) { &lt;span class="hljs-comment">//right-bottom&lt;/span> path.moveTo(realPosX-&lt;span class="hljs-number">5&lt;/span>, realPosY+&lt;span class="hljs-number">5&lt;/span>); path.lineTo(realPosX-&lt;span class="hljs-number">15&lt;/span>, realPosY+&lt;span class="hljs-number">15&lt;/span>); path.lineTo(realPosX - &lt;span class="hljs-number">15&lt;/span>, realPosY - &lt;span class="hljs-number">15&lt;/span>); rectF = &lt;span class="hljs-keyword">new&lt;/span> RectF(realPosX-&lt;span class="hljs-number">200&lt;/span>, realPosY-&lt;span class="hljs-number">40&lt;/span>, realPosX-&lt;span class="hljs-number">15&lt;/span>, realPosY + &lt;span class="hljs-number">30&lt;/span>); canvas.drawRoundRect(rectF, &lt;span class="hljs-number">15&lt;/span>, &lt;span class="hljs-number">15&lt;/span>, mPaint); mPaint.reset(); mPaint.setColor(Color.RED); mPaint.setTextSize(mXYTitleTextSize - &lt;span class="hljs-number">5&lt;/span>); canvas.drawText(&lt;span class="hljs-string">"("&lt;/span>+mMeasureXpos+&lt;span class="hljs-string">", "&lt;/span>+mMeasureYpos+&lt;span class="hljs-string">")"&lt;/span>, realPosX-&lt;span class="hljs-number">180&lt;/span>, realPosY, mPaint); } &lt;span class="hljs-keyword">else&lt;/span> &lt;span class="hljs-keyword">if&lt;/span> (realPosX &gt; centerPos[&lt;span class="hljs-number">0&lt;/span>] && realPosY &lt; centerPos[&lt;span class="hljs-number">1&lt;/span>]) { &lt;span class="hljs-comment">//right-top&lt;/span> path.moveTo(realPosX-&lt;span class="hljs-number">5&lt;/span>, realPosY+&lt;span class="hljs-number">5&lt;/span>); path.lineTo(realPosX-&lt;span class="hljs-number">15&lt;/span>, realPosY+&lt;span class="hljs-number">15&lt;/span>); path.lineTo(realPosX - &lt;span class="hljs-number">15&lt;/span>, realPosY - &lt;span class="hljs-number">15&lt;/span>); rectF = &lt;span class="hljs-keyword">new&lt;/span> RectF(realPosX-&lt;span class="hljs-number">200&lt;/span>, realPosY - &lt;span class="hljs-number">20&lt;/span>, realPosX-&lt;span class="hljs-number">15&lt;/span>, realPosY + &lt;span class="hljs-number">50&lt;/span>); canvas.drawRoundRect(rectF, &lt;span class="hljs-number">15&lt;/span>, &lt;span class="hljs-number">15&lt;/span>, mPaint); mPaint.reset(); mPaint.setColor(Color.RED); mPaint.setTextSize(mXYTitleTextSize - &lt;span class="hljs-number">5&lt;/span>); canvas.drawText(&lt;span class="hljs-string">"("&lt;/span>+mMeasureXpos+&lt;span class="hljs-string">", "&lt;/span>+mMeasureYpos+&lt;span class="hljs-string">")"&lt;/span>, realPosX-&lt;span class="hljs-number">180&lt;/span>, realPosY+&lt;span class="hljs-number">30&lt;/span>, mPaint); } path.close(); mPaint.setColor(Color.WHITE); mPaint.setStyle(Paint.Style.FILL_AND_STROKE); canvas.drawPath(path, mPaint); } &lt;span class="hljs-comment">//设置当前比值&lt;/span> &lt;span class="hljs-keyword">public&lt;/span> &lt;span class="hljs-keyword">void&lt;/span> &lt;span class="hljs-title">updateValues&lt;/span>(&lt;span class="hljs-keyword">int&lt;/span> x, &lt;span class="hljs-keyword">int&lt;/span> y) { mMeasureXpos = x; mMeasureYpos = y; postInvalidate(); } &lt;span class="hljs-comment">//设置XY轴顶角的title字体大小&lt;/span> &lt;span class="hljs-keyword">public&lt;/span> &lt;span class="hljs-keyword">void&lt;/span> &lt;span class="hljs-title">setTitleTextSize&lt;/span>(&lt;span class="hljs-keyword">int&lt;/span> size) { mXYTitleTextSize = size; } &lt;span class="hljs-comment">//初始化X轴的坐标区间点值,可以不均等分&lt;/span> &lt;span class="hljs-keyword">public&lt;/span> &lt;span class="hljs-keyword">void&lt;/span> &lt;span class="hljs-title">initXLevelOffset&lt;/span>(ArrayList&lt;Integer&gt; list) { mXLevel.clear(); mXLevel.addAll(list); } &lt;span class="hljs-comment">//初始化Y轴的坐标区间点值,可以不均等分&lt;/span> &lt;span class="hljs-keyword">public&lt;/span> &lt;span class="hljs-keyword">void&lt;/span> &lt;span class="hljs-title">initYLevelOffset&lt;/span>(ArrayList&lt;Integer&gt; list) { mYLevel.clear(); mYLevel.addAll(list); } &lt;span class="hljs-comment">//初始化每个区间的提示文字,如果不想显示可以设置""&lt;/span> &lt;span class="hljs-keyword">public&lt;/span> &lt;span class="hljs-keyword">void&lt;/span> &lt;span class="hljs-title">initGridLevelText&lt;/span>(ArrayList&lt;String&gt; list) { mGridLevelText.clear(); mGridLevelText.addAll(list); } &lt;span class="hljs-comment">//初始化每个区间的颜色&lt;/span> &lt;span class="hljs-keyword">public&lt;/span> &lt;span class="hljs-keyword">void&lt;/span> &lt;span class="hljs-title">initGridColorLevel&lt;/span>(ArrayList&lt;Integer&gt; list) { mGridColorLevel.clear(); mGridColorLevel.addAll(list); } &lt;span class="hljs-comment">//初始化每个区间的提示文字颜色&lt;/span> &lt;span class="hljs-keyword">public&lt;/span> &lt;span class="hljs-keyword">void&lt;/span> &lt;span class="hljs-title">initGridTxtColorLevel&lt;/span>(ArrayList&lt;Integer&gt; list) { mGridTxtColorLevel.clear(); mGridTxtColorLevel.addAll(list); } &lt;span class="hljs-comment">//初始化XY轴title&lt;/span> &lt;span class="hljs-keyword">public&lt;/span> &lt;span class="hljs-keyword">void&lt;/span> &lt;span class="hljs-title">initTitleXY&lt;/span>(String x, String y) { mTitleX = x; mTitleY = y; } }` 再来看下布局文件: ...

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

Android屏幕适配全攻略(最权威的官方适配指导)

转载:http://blog.csdn.net/zhaokaiqiang1992 Android的屏幕适配一直以来都在折磨着我们这些开发者,本篇文章以Google的官方文档为基础,全面而深入的讲解了Android屏幕适配的原因、重要概念、解决方案及最佳实践,我相信如果你能认真的学习本文,对于Android的屏幕适配,你将有所收获! - [Android屏幕适配出现的原因](http://blog.csdn.net/zhaokaiqiang1992/article/details/45419023#android屏幕适配出现的原因) - [重要概念](http://blog.csdn.net/zhaokaiqiang1992/article/details/45419023#重要概念) - [屏幕尺寸](http://blog.csdn.net/zhaokaiqiang1992/article/details/45419023#屏幕尺寸) - [屏幕分辨率](http://blog.csdn.net/zhaokaiqiang1992/article/details/45419023#屏幕分辨率) - [屏幕像素密度](http://blog.csdn.net/zhaokaiqiang1992/article/details/45419023#屏幕像素密度) - [dpdipdpisppx](http://blog.csdn.net/zhaokaiqiang1992/article/details/45419023#dpdipdpisppx) - [mdpihdpixdpixxdpi](http://blog.csdn.net/zhaokaiqiang1992/article/details/45419023#mdpihdpixdpixxdpi) - [解决方案](http://blog.csdn.net/zhaokaiqiang1992/article/details/45419023#解决方案) - [支持各种屏幕尺寸](http://blog.csdn.net/zhaokaiqiang1992/article/details/45419023#支持各种屏幕尺寸) - [使用wrap_contentmatch_parentweight](http://blog.csdn.net/zhaokaiqiang1992/article/details/45419023#使用wrapcontentmatchparentweight) - [使用相对布局禁用绝对布局](http://blog.csdn.net/zhaokaiqiang1992/article/details/45419023#使用相对布局禁用绝对布局) - [使用限定符](http://blog.csdn.net/zhaokaiqiang1992/article/details/45419023#使用限定符) - [使用尺寸限定符](http://blog.csdn.net/zhaokaiqiang1992/article/details/45419023#使用尺寸限定符) - [使用最小宽度限定符](http://blog.csdn.net/zhaokaiqiang1992/article/details/45419023#使用最小宽度限定符) - [使用布局别名](http://blog.csdn.net/zhaokaiqiang1992/article/details/45419023#使用布局别名) - [使用屏幕方向限定符](http://blog.csdn.net/zhaokaiqiang1992/article/details/45419023#使用屏幕方向限定符) - [使用自动拉伸位图](http://blog.csdn.net/zhaokaiqiang1992/article/details/45419023#使用自动拉伸位图) - [支持各种屏幕密度](http://blog.csdn.net/zhaokaiqiang1992/article/details/45419023#支持各种屏幕密度) - [使用非密度制约像素](http://blog.csdn.net/zhaokaiqiang1992/article/details/45419023#使用非密度制约像素) - [提供备用位图](http://blog.csdn.net/zhaokaiqiang1992/article/details/45419023#提供备用位图) - [实施自适应用户界面流程](http://blog.csdn.net/zhaokaiqiang1992/article/details/45419023#实施自适应用户界面流程) - [确定当前布局](http://blog.csdn.net/zhaokaiqiang1992/article/details/45419023#确定当前布局) - [根据当前布局做出响应](http://blog.csdn.net/zhaokaiqiang1992/article/details/45419023#根据当前布局做出响应) - [重复使用其他活动中的片段](http://blog.csdn.net/zhaokaiqiang1992/article/details/45419023#重复使用其他活动中的片段) - [处理屏幕配置变化](http://blog.csdn.net/zhaokaiqiang1992/article/details/45419023#处理屏幕配置变化) - [最佳实践](http://blog.csdn.net/zhaokaiqiang1992/article/details/45419023#最佳实践) - [关于高清设计图尺寸](http://blog.csdn.net/zhaokaiqiang1992/article/details/45419023#关于高清设计图尺寸) - [ImageView的ScaleType属性](http://blog.csdn.net/zhaokaiqiang1992/article/details/45419023#imageview的scaletype属性) - [动态设置](http://blog.csdn.net/zhaokaiqiang1992/article/details/45419023#动态设置) - [更多参考资料](http://blog.csdn.net/zhaokaiqiang1992/article/details/45419023#更多参考资料) Android屏幕适配出现的原因 在我们学习如何进行屏幕适配之前,我们需要先了解下为什么Android需要进行屏幕适配。 由于Android系统的开放性,任何用户、开发者、OEM厂商、运营商都可以对Android进行定制,修改成他们想要的样子。 但是这种“碎片化”到底到达什么程度呢? 在2012年,OpenSignalMaps(以下简称OSM)发布了第一份Android碎片化报告,统计数据表明, 2012年,支持Android的设备共有3997种。 2013年,支持Android的设备共有11868种。 2014年,支持Android的设备共有18796种。 下面这张图片所显示的内容足以充分说明当今Android系统碎片化问题的严重性,因为该图片中的每一个矩形都代表着一种Android设备。 ...

2015年6月6日 · 16 分钟 · 天边的星星

通过 Navigation View 创建导航抽屉

随着 Google I/O 2015,新的 Android Design Support Library 也出现了。 Android Design Support Library 给开发者带来了一些重要的 Material Design 组件,并且向下兼容到 Android 2.1,Navigation View就是其中之一,可用于方便地创建导航抽屉。 效果如下: 因为需要向下兼容,所以以下内容的 Activity 均继承于AppCompatActivity,使用的主题的父主题均为 AppCompat 的主题。 基本步骤 通过Navigation View创建导航抽屉首先自然需要引入这个支持包: compile 'com.android.support:design:22.2.0' 布局文件中加入以下代码: ``` <android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent"> <span class="c">&lt;!-- 需要呈现的内容 --&gt;</span> <span class="nt">&lt;android.support.design.widget.NavigationView</span> <span class="na">android:layout_width=</span><span class="s">"wrap_content"</span> <span class="na">android:layout_height=</span><span class="s">"match_parent"</span> <span class="na">android:layout_gravity=</span><span class="s">"start"</span> <span class="na">app:headerLayout=</span><span class="s">"@layout/drawer_header"</span> <span class="na">app:menu=</span><span class="s">"@menu/drawer"</span><span class="nt">/&gt;</span> </android.support.v4.widget.DrawerLayout> </div> 其中`app:headerLayout`用于指定一个任意的布局,作为导航抽屉的顶部,如效果图中的紫色带 Username 字样部分。 而`app:menu`用于指定导航抽屉的菜单项,具体代码如下: <div class="codehilite"> <menu xmlns:android=“http://schemas.android.com/apk/res/android"> <span class="nt">&lt;group</span> <span class="na">android:checkableBehavior=</span><span class="s">"single"</span><span class="nt">&gt;</span> <span class="nt">&lt;item</span> <span class="na">android:id=</span><span class="s">"@+id/nav_home"</span> <span class="na">android:icon=</span><span class="s">"@drawable/ic_dashboard"</span> <span class="na">android:title=</span><span class="s">"Home"</span> <span class="nt">/&gt;</span> <span class="nt">&lt;item</span> <span class="na">android:id=</span><span class="s">"@+id/nav_messages"</span> <span class="na">android:icon=</span><span class="s">"@drawable/ic_event"</span> <span class="na">android:title=</span><span class="s">"Messages"</span> <span class="nt">/&gt;</span> <span class="nt">&lt;item</span> <span class="na">android:id=</span><span class="s">"@+id/nav_friends"</span> <span class="na">android:icon=</span><span class="s">"@drawable/ic_headset"</span> <span class="na">android:title=</span><span class="s">"Friends"</span> <span class="nt">/&gt;</span> <span class="nt">&lt;item</span> <span class="na">android:id=</span><span class="s">"@+id/nav_discussion"</span> <span class="na">android:icon=</span><span class="s">"@drawable/ic_forum"</span> <span class="na">android:title=</span><span class="s">"Discussion"</span> <span class="nt">/&gt;</span> <span class="nt">&lt;/group&gt;</span> <span class="nt">&lt;item</span> <span class="na">android:title=</span><span class="s">"Sub items"</span><span class="nt">&gt;</span> <span class="nt">&lt;menu&gt;</span> <span class="nt">&lt;item</span> <span class="na">android:icon=</span><span class="s">"@drawable/ic_dashboard"</span> <span class="na">android:title=</span><span class="s">"Sub item 1"</span> <span class="nt">/&gt;</span> <span class="nt">&lt;item</span> <span class="na">android:icon=</span><span class="s">"@drawable/ic_forum"</span> <span class="na">android:title=</span><span class="s">"Sub item 2"</span> <span class="nt">/&gt;</span> <span class="nt">&lt;/menu&gt;</span> <span class="nt">&lt;/item&gt;</span> </menu> ...

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

Android开发技术周报

教程 在 Android 中使用 data-binder 绑定布局 xml 与数据在前几天的 Google IO 2015 中,Google 在 support-v7 中新增了 data-binder,使用 data-binder 可以直接在布局的 xml 中绑定布局与数据,从而简化代码。因为 data-binder 是包含在 support-v7 包里面的,所以可以向下兼容到最低 Android 2.1 (API level 7+). Android的材料设计兼容库这个兼容库很容易和之前的 Android Support Library 22.1混淆,都是兼容库,区别是这个库多了个Design。 Android Support Library 22.1只是支持了一些基本控件的材料设计化,但是这个库更多的是对一些特效的实现,这个库和github上的很多开源项目是有很大关系的,material design的很多效果,同一种效果在github上有太多的实现,现在官方把部分效果标准化了。 Google I/O 2015 为 Android 开发者带来了哪些福利?先得说的便是,今年的更新有些不给力,至少显得不够 Geek。我也不打算接着盘点一些在 Keynote 中的资讯,想必很多人在各个站点已经看过不知道多少遍了,我接下来想说的一些是关于这次 Google I/O 为 Android 开发者们带来了怎样的福利。 Nexus6 With Android M开启多窗口模式昨天的Google IO之后,Google放出了Android M Preview for Nexus6. 固件大家可以去Google的官网去下,下好了刷完之后,就可以体验一下最新的Android M了。 使用Android Accessibility实现免Root自动批量安装功能对于国内Android设备,应用的自动批量安装/更新一直是一个痛点,在之前,第三方应用商店通常要求设备Root,然后调用系统的PackageManagerService命令行来实现后台安装。最近,豌豆荚利用Android Accessibility(辅助功能)在业内率先实现了免Root自动批量安装功能。 Android 9patch 图片解析堆溢出漏洞分析前谷歌公开了一个今年1月份更新的漏洞。这个漏洞修复了一个存在于Android 5.1版本以下图片渲染的问题,可以查看相关链接。9patch是Android上特有的一种图片格式,就是在普通的png图片的基础了增加了一些像素的边框,使之具有可随意拉伸、缩放的功能。 Gradle 修改 Maven 仓库地址要先说明的是本文说的“渠道”单指在AndroidManifest.xml 用定义的一个标识字符串(如友盟统计)。在代码或者通过其他文件定义的方式殊途同归。说起 Android 多渠道打包,真是八仙过海各显神通:有手动一个个耐心打包的,有用Ant或Maven重复跑编译任务的,有用apktool解包后再修改重打包的,有在build.gradle定义一堆flavor的,乃至有通过apk里META-INF/下的空文件来定义渠道的。 谷歌推荐的技术能力提升指南打好扎实的计算机科学基础对于成为一个成功的软件工程师是非常重要的。本指南主要关于如何提升自己的技术能力,非常适合学生用于制定教学课程,当然这里提供的网络资源,并不意味着就可以完全取代现有的课程,正式的课程安排还是要学的(除非你不想拿到毕业证书)。 Android应用setContentView与LayoutInflater加载解析机制源码分析其实之所以要说这个话题有几个原因:1.理解xml等控件是咋被显示的原理,通常大家写代码都是直接在onCreate里setContentView就完事,没怎么关注其实现原理。2.前面分析《Android触摸屏事件派发机制详解与源码分析三(Activity篇)》时提到了一些关于布局嵌套的问题,当时没有深入解释。 Android应用程序UI硬件加速渲染的预加载资源地图集服务(Asset Atlas Service)分析我们知道,Android系统在启动的时候,会对一些系统资源进行预加载。这样不仅使得应用程序在需要时可以快速地访问这些资源,还使得这些资源能够在不同应用程序之间进行共享。在硬件加速渲染环境中,这些预加载资源还有进一步优化的空间。 Android 不规则封闭区域填充 手指秒变油漆桶图像的填充有2种经典算法。 一种是种子填充法。种子填充法理论上能够填充任意区域和图形,但是这种算法存在大量的反复入栈和大规模的递归,降低了填充效率。 另一种是扫描线填充法。 Android屏幕适配全攻略(最权威的官方适配指导)Android的屏幕适配一直以来都在折磨着我们这些开发者,本篇文章以Google的官方文档为基础,全面而深入的讲解了Android屏幕适配的原因、重要概念、解决方案及最佳实践,我相信如果你能认真的学习本文,对于Android的屏幕适配,你将有所收获! Android系统Root与静默安装静默安装,指的是安装时无需任何用户干预,直接按默认设置安装应用。因为,它的无需用户干预,很多情况下变成了用户压根不知道,应用不知不觉就安装上了。是在推广上极为流氓的手段,很类似PC上的捆绑安装。正因为静默安装时极为流氓的推广行为,所以,其推广价格也极其高。 代码&开源库 cheesesquareAndroid Design library的示例。 Android-NiceTab支持小圆点,背景模糊,图标cross fade等效果的自定义Tab. BGARefreshLayout-Android多种下拉刷新效果、上拉加载更多、可配置自定义头部广告位。 AnimateCheckBox自定义CheckBox,选中未选中的切换动画很赞。 SelectorInjection一个强大的selector注入器,它可以让view自动产生selector状态,免去了你写selector的麻烦。 ShareLoginLib第三方分享登录组件. AndroidGradleTemplateGradle + Android Studio + Robolectric + Espresso + Mockito + EasyMock/PowerMock + JaCoCo Android-Task可以在后台执行Task的Library。 FORMWatchFaceAnddroid Wear 表盘。 MultiThreadDownloader逻辑比较简单但实用的Android多线程断点续传下载器。 DatePickerAndroid日历选择器。 Material Calendar ViewMaterial Design风格的日历控件。 工具 Android Studio 1.3 Preview1The new version contains many new features.: New Allocation Tracker New Heap dump Viewer Many new code inspections to enforce framework and support library threading annotations, range annotations, call super, check result, etc. Missing permission checks and unhandled revocable permission checks Android M preview data binding Support Support for adding Google Services to the project in the project structure dialog (especially for Analytics) SDK update notifications, and brand new integrated SDK manager UI New quickfixes, such as automatic generation of a Parcelable implementation Many built-in live code templates Many other smaller features and bug fixes As announced at Google I/O, Android Studio 1.3 will include C/C++ support as well, but that is not included in the first couple of preview buil * [Android NDK r10e][28]Release Notes: <http://developer.android.com/intl/zh-cn/ndk/downloads/index.html#rel> * [GsonFormat][29]根据Gson库使用的要求,将JSONObject格式的String 解析成实体的 Android Studio 插件。 ### 视频 {#} 1. [Google I/O 2015的各种视频][30]墙内Google I/O 2015的各种视频,没有看的小伙伴赶紧去瞅瞅看吧。 2. [Android QQ音乐架构演进][31]随着移动互联网的不断蓬勃发展,更多用户在移动设备上使用音乐服务,QQ音乐移动客户端使用用户数也在屡创新高,QQ音乐为了达到更好的用户体验并实现用户的新需求,原有的框架已经不能优雅的实现新需求和优先。如何优雅的实现各种需求并在性能和稳定性得到提高,QQ音乐Android开发团队通过以下的篇章给大家分享QQ音乐架构演进带来的痛与乐。 转自:http://androidweekly.cn/android-dev-weekly-issue33/?utm_source=tuicool

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

Android中的dispatchTouchEvent()、onInterceptTouchEvent()和onTouchEvent()

Android中触摸事件传递过程中最重要的是dispatchTouchEvent()、onInterceptTouchEvent()和onTouchEvent()方法。这个是困扰初学者的问题之一,我开始也是。这里记录一下dispatchTouchEvent()、onInterceptTouchEvent()和onTouchEvent()的处理过程,以供记忆。 dispatchTouchEvent是处理触摸事件分发,事件(多数情况)是从Activity的dispatchTouchEvent开始的。执行 super.dispatchTouchEvent(ev),事件向下分发。 onInterceptTouchEvent是ViewGroup提供的方法,默认返回false,返回true表示拦截。 onTouchEvent是View中提供的方法,ViewGroup也有这个方法,view中不提供onInterceptTouchEvent。view中默认返回true,表示消费了这个事件。 View里,有两个回调函数 : **[java]** [view plain](http://blog.csdn.net/xyz_lmn/article/details/12517911#)[copy](http://blog.csdn.net/xyz_lmn/article/details/12517911#)[print](http://blog.csdn.net/xyz_lmn/article/details/12517911#)[?](http://blog.csdn.net/xyz_lmn/article/details/12517911#)[![在CODE上查看代码片](https://code.csdn.net/assets/CODE_ico.png)](https://code.csdn.net/snippets/219578)[![派生到我的代码片](https://code.csdn.net/assets/ico_fork.svg)](https://code.csdn.net/snippets/219578/fork) <div> <embed id="ZeroClipboardMovie_1" src="http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf" type="application/x-shockwave-flash" width="27" height="15" align="middle" name="ZeroClipboardMovie_1"> </embed> </div> </div> - <span class="keyword">public</span> <span class="keyword">boolean</span> dispatchTouchEvent(MotionEvent ev); - <span class="keyword">public</span> <span class="keyword">boolean</span> onTouchEvent(MotionEvent ev); ViewGroup里,有三个回调函数 : **[java]** [view plain](http://blog.csdn.net/xyz_lmn/article/details/12517911#)[copy](http://blog.csdn.net/xyz_lmn/article/details/12517911#)[print](http://blog.csdn.net/xyz_lmn/article/details/12517911#)[?](http://blog.csdn.net/xyz_lmn/article/details/12517911#)[![在CODE上查看代码片](https://code.csdn.net/assets/CODE_ico.png)](https://code.csdn.net/snippets/219578)[![派生到我的代码片](https://code.csdn.net/assets/ico_fork.svg)](https://code.csdn.net/snippets/219578/fork) <div> <embed id="ZeroClipboardMovie_2" src="http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf" type="application/x-shockwave-flash" width="27" height="15" align="middle" name="ZeroClipboardMovie_2"> </embed> </div> </div> - <span class="keyword">public</span> <span class="keyword">boolean</span> dispatchTouchEvent(MotionEvent ev); - <span class="keyword">public</span> <span class="keyword">boolean</span> onInterceptTouchEvent(MotionEvent ev); - <span class="keyword">public</span> <span class="keyword">boolean</span> onTouchEvent(MotionEvent ev); 在Activity里,有两个回调函数 : ...

2015年6月1日 · 1 分钟 · 天边的星星

ScrollView下,ListView生存之道

首先讲一下我遇到的需求吧,页面是这样的,上边有东西,中间是列表,下边还有东西。首先我看到列表立刻就想到了用ListView,但是页面有限,只能用ScrollView包一下。想到就做呗。我就在ScrollView里面加了一个ListView, ListView设置的是wapcontent,这样就出现了ListView数据只显示出了一行。好的,解决问题的方案就来了。 一.设置scrollView中的ListView内容全部显示,不能滑动,将滑动交给scrollView去做。 做法:在设置adapter之前,重新计算ListView的高度,我这里写了一个方法: java代码 [![](http://www.eyeandroid.com/source/plugin/milu_hightLight/help.png)](http://www.eyeandroid.com/) - <span class="comment">/**</span> - <span class="comment">* 动态设置listView的高度</span> - <span class="comment">* count 总条目</span> - <span class="comment">*/</span> - <span class="keyword">private</span> <span class="keyword">void</span> setListViewHeight(ListView listView, BaseAdapter adapter, - <span class="keyword">int</span> count) { - <span class="keyword">int</span> totalHeight = <span class="number"></span>; - <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number"></span>; i < count; i++) { - View listItem = adapter.getView(i, <span class="keyword">null</span>, listView); - listItem.measure(<span class="number"></span>, <span class="number"></span>); - totalHeight += listItem.getMeasuredHeight(); - } - - ViewGroup.LayoutParams params = listView.getLayoutParams(); - params.height = totalHeight + (listView.getDividerHeight() * count); - listView.setLayoutParams(params); - } 这样做的前提条件是布局文件中ListView的高度要指定,这样才能重新计算,不要设成wapcontent! ...

2015年6月1日 · 5 分钟 · 天边的星星

ViewStub、include、merge使用与源码分析

在开发中UI布局是我们都会遇到的问题,随着UI越来越多,布局的重复性、复杂度也会随之增长。Android官方给了几个优化的方法,但是网络上的资料基本上都是对官方资料的翻译,这些资料都特别的简单,经常会出现问题而不知其所以然。这篇文章就是对这些问题的更详细的说明,也欢迎大家多留言交流。 一、include 首先用得最多的应该是include,按照官方的意思,include就是为了解决重复定义相同布局的问题。例如你有五个界面,这五个界面的顶部都有布局一模一样的一个返回按钮和一个文本控件,在不使用include的情况下你在每个界面都需要重新在xml里面写同样的返回按钮和文本控件的顶部栏,这样的重复工作会相当的恶心。使用include标签,我们只需要把这个会被多次使用的顶部栏独立成一个xml文件,然后在需要使用的地方通过include标签引入即可。其实就相当于C语言、C++中的include头文件一样,我们把一些常用的、底层的API封装起来,然后复用,需要的时候引入它即可,而不必每次都自己写一遍。示例如下 : my_title_layout.xml `&lt;?xml version="1.0" encoding="utf-8"?&gt; &lt;RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:id="@+id/my_title_parent_id" android:layout_height="wrap_content" &gt; &lt;ImageButton android:id="@+id/back_btn" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/ic_launcher" /&gt; &lt;TextView android:id="@+id/title_tv" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerVertical="true" android:layout_marginLeft="20dp" android:layout_toRightOf="@+id/back_btn" android:gravity="center" android:text="我的title" android:textSize="18sp" /&gt; &lt;/RelativeLayout&gt; ` include布局文件: `&lt;?xml version="1.0" encoding="utf-8"?&gt; &lt;LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" &gt; &lt;include android:id="@+id/my_title_ly" android:layout_width="match_parent" android:layout_height="wrap_content" layout="@layout/my_title_layout" /&gt; &lt;!-- 代码省略 --&gt; &lt;/LinearLayout&gt; ` 这样我们就可以使用my_title_layout了。 ...

2015年5月27日 · 8 分钟 · 天边的星星