Android 5.x新特性之elevation(阴影),tinting(着色)以及clipping(剪裁)

研究了Google I/O 2014 发布 Material Design设计,人性化的风格,丰富的色彩,使人机交互更完美。中文学习地址http://wiki.jikexueyuan.com/project/material-design/(这个好像是极客学院翻译的),当然如果你的引文OK的话,也可以去看官方英文文档http://www.google.com/design/spec/material-design/

1. 阴影以及高度--elevation

继续阅读Android 5.x新特性之elevation(阴影),tinting(着色)以及clipping(剪裁)

Android获取SD卡路径及SDCard内存的方法

Android获取SD卡路径及SDCard内存的方法。

代码如下:

public String getSDPath(){ 
       File sdDir = null; 
       boolean sdCardExist = Environment.getExternalStorageState()   
       .equals(android.os.Environment.MEDIA_MOUNTED);//判断sd卡是否存在
       if(sdCardExist)   
       {                               
         sdDir = Environment.getExternalStorageDirectory();//获取跟目录
      }   
       return sdDir.toString(); 
}

然后:在后面加上斜杠,在加上文件名

如下:

String fileName = getSDPath() +"/" + name;//以name存在目录中
1、讲述 Environment 类
Environment 是一个提供访问环境变量的类。 
Environment 包含常量:
MEDIA_BAD_REMOVAL 
解释:返回getExternalStorageState() ,表明SDCard 被卸载前己被移除
MEDIA_CHECKING 
解释:返回getExternalStorageState() ,表明对象正在磁盘检查。
MEDIA_MOUNTED 
解释:返回getExternalStorageState() ,表明对象是否存在并具有读/写权限
MEDIA_MOUNTED_READ_ONLY 
解释:返回getExternalStorageState() ,表明对象权限为只读
MEDIA_NOFS 
解释:返回getExternalStorageState() ,表明对象为空白或正在使用不受支持的文件系统。
MEDIA_REMOVED 
解释:返回getExternalStorageState() ,如果不存在 SDCard 返回
MEDIA_SHARED 
解释:返回getExternalStorageState() ,如果 SDCard 未安装 ,并通过 USB 大容量存储共享 返回
MEDIA_UNMOUNTABLE 
解释:返回getExternalStorageState() ,返回 SDCard 不可被安装 如果 SDCard 是存在但不可以被安装
MEDIA_UNMOUNTED 
解释:返回getExternalStorageState() ,返回 SDCard 已卸掉如果 SDCard   是存在但是没有被安装
Environment 常用方法:
方法:getDataDirectory() 
解释:返回 File ,获取 Android 数据目录。
方法:getDownloadCacheDirectory() 
解释:返回 File ,获取 Android 下载/缓存内容目录。
方法:getExternalStorageDirectory() 
解释:返回 File ,获取外部存储目录即 SDCard
方法:getExternalStoragePublicDirectory(String type) 
解释:返回 File ,取一个高端的公用的外部存储器目录来摆放某些类型的文件
方法:getExternalStorageState() 
解释:返回 File ,获取外部存储设备的当前状态
方法:getRootDirectory() 
解释:返回 File ,获取 Android 的根目录
2、讲述 StatFs 类 
StatFs 一个模拟linux的df命令的一个类,获得SD卡和手机内存的使用情况 
StatFs 常用方法:
getAvailableBlocks() 
解释:返回 Int ,获取当前可用的存储空间
getBlockCount() 
解释:返回 Int ,获取该区域可用的文件系统数
getBlockSize() 
解释:返回 Int ,大小,以字节为单位,一个文件系统
getFreeBlocks() 
解释:返回 Int ,该块区域剩余的空间
restat(String path) 
解释:执行一个由该对象所引用的文件系统
3、完整例子读取 SDCard 内存 
存储卡在 Android 手机上是可以随时插拔的,每次的动作都对引起操作系统进行 ACTION_BROADCAST,本例子将使用上面学到的方法,计算出 SDCard 的剩余容量和总容量。代码如下:

代码如下:

package com.terry;

import java.io.File;
import java.text.DecimalFormat;
import android.R.integer;
import android.app.Activity;
import android.os.Bundle;
import android.os.Environment;
import android.os.StatFs;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;

public class getStorageActivity extends Activity {
    
    private Button myButton;
    
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        findView();
        viewHolder.myButton.setOnClickListener(new OnClickListener() {
            
            @Override
            public void onClick(View arg0) {
                // TODO Auto-generated method stub 
                getSize();
            }
        });
    }
    
    void findView(){
        viewHolder.myButton=(Button)findViewById(R.id.Button01);
        viewHolder.myBar=(ProgressBar)findViewById(R.id.myProgressBar);
        viewHolder.myTextView=(TextView)findViewById(R.id.myTextView);
    }
    
    void getSize(){
        viewHolder.myTextView.setText("");
        viewHolder.myBar.setProgress(0);
        //判断是否有插入存储卡 
        if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){
            File path =Environment.getExternalStorageDirectory();
            //取得sdcard文件路径 
            StatFs statfs=new StatFs(path.getPath());
            //获取block的SIZE 
            long blocSize=statfs.getBlockSize();
            //获取BLOCK数量 
            long totalBlocks=statfs.getBlockCount();
            //己使用的Block的数量 
            long availaBlock=statfs.getAvailableBlocks();
            String[] total=filesize(totalBlocks*blocSize);
            String[] availale=filesize(availaBlock*blocSize);
            //设置进度条的最大值 
            int maxValue = Integer.parseInt(availale[0]) * viewHolder.myBar.getMax() / Integer.parseInt(total[0]);
            viewHolder.myBar.setProgress(maxValue);
            String Text="总共:" + total[0]+total[1]+"/n" + "可用:"+availale[0] + availale[1];
            viewHolder.myTextView.setText(Text);
        } else if(Environment.getExternalStorageState().equals(Environment.MEDIA_REMOVED)){
                Toast.makeText(getStorageActivity.this, "没有sdCard",1000).show();
            }
    }
    
    //返回数组,下标1代表大小,下标2代表单位 KB/MB 
    String[] filesize(long size){
        String str="";
        if(size>=1024){
            str="KB";
            size/=1024;
            if(size>=1024){
                str="MB";
                size/=1024;
            }
        }
        DecimalFormat formatter=new DecimalFormat();
        formatter.setGroupingSize(3);
        String result[] =new String[2];
        result[0]=formatter.format(size);
        result[1]=str;
        return result;
    }
}

参考链接


Android获取SD卡路径及SDCard内存的方法

Android 获取View在屏幕中的位置

Android里面提供了一些方法可以获取View在屏幕中的位置。

getLocationOnScreen,计算该视图在全局坐标系中的x,y值,获取在当前屏幕内的绝对坐标(该值从屏幕顶端算起,包括了通知栏高度)。

getLocationInWindow,计算该视图在它所在的widnow的坐标x,y值。

getLeft, getTop, getBottom, getRight, 这一组是获取相对在它父亲布局里的坐标。

注意:如果在Activity的OnCreate()事件输出那些参数,是全为0,要等UI控件都加载完了才能获取到这些数据。

button2.setOnClickListener(new OnClickListener() {
 
            @Override
            public void onClick(View v) {
                int[] location = new int[2];
                v.getLocationOnScreen(location);
                x = location[0];
                y = location[1];
                Log.d("test", "Screenx--->" + x + "  " + "Screeny--->" + y);
                v.getLocationInWindow(location);
                x = location[0];
                y = location[1];
                Log.d("test", "Window--->" + x + "  " + "Window--->" + y);
                Log.d("test", "left:" + v.getLeft());
                Log.d("test", "right:" + v.getRight());
                Log.d("test", "Top:" + v.getTop());
                Log.d("test", "Bottom:" + v.getBottom());
                Log.d("test", "Width:"+v.getWidth());
                Log.d("test", "Height:"+v.getHeight());
            }

getDimension()、getDimensionPixelSize()和getDimensionPixelOffset()的区别

今天写代码的时候,需要从资源文件中读取一个长度值,用来设置控件的宽高,误以为getDimension()返回的就是资源里定义的dp值,后来发现不是我理解的那样。下面介绍一下getDimension()、getDimensionPixelSize()和getDimensionPixelOffset()的区别;

先看我在资源中的定义:

<dimen name="guide_first_cover_image_width">171dp</dimen>

然后打印出它们的值看看

QLog.i(TAG,String.format("getDimension: %f",mContext.getResources().getDimension(R.dimen.guide_first_cover_image_width)) );

QLog.i(TAG,String.format("getDimensionPixelSize: %d",mContext.getResources().getDimensionPixelSize(R.dimen.guide_first_cover_image_width)));

QLog.i(TAG,String.format("getDimensionPixelOffset:%d",mContext.getResources().getDimensionPixelOffset(R.dimen.guide_first_cover_image_width)));

结果如下:

getDimension: 256.500000

getDimensionPixelSize: 257

getDimensionPixelOffset:256

我的模拟器是480*800的,屏幕密度是1.5,从打印结果就可以推知,getDimension()、getDimensionPixelSize()和getDimenPixelOffset()的结果值都是将资源文件中定义的dip值乘以屏幕密度,即171*1.5=256.5,只是getDimension()返回的是float,其余两个返回的是int, 其中getDimensionPixelSize()返回的是实际数值的四舍五入,而getDimensionPixelOffset返回的是实际数值去掉后面的小数点; 再跟踪代码查看这三个函数的具体实现,可以了解得更具体。总然而之,这三个函数返回的都是dip值乘以屏幕密度,如果你在资源文件中定义的长度单位不是dip,而是px的话,程序会直接抛出异常。

参考链接


getDimension()、getDimensionPixelSize()和getDimensionPixelOffset()的区别

Android Scroller实现View弹性滑动完全解析

说起View的滑动效果,实现的方法有多种,例如使用动画,或者通过改变View的布局参数,其实除了这两种外,在AndroidView已经为我们提供了scrollTo()scrollBy()方法来实现滑动效果,这两个方法也是我们接下来要重点讨论的...

但是呢,这两个方法实现的滑动效果都是瞬时完成的,并不能带来良好的体验哦,所以此时就需要我们的重量级嘉宾Scroller登场了,通过Scroller可以实现View在一定时间间隔内的弹性滑动,以带来更好的视觉体验,这么说可能有点抽象,举个例子,当我们使用ViewPager时,手指滑动一段距离松开后ViewPager会自动的滑动一段距离后停止,这个效果就是通过Scroller实现的,这样说应该能更好的理解Scroller的用途了。

本着从理论到实践的原则,我们来一步步的揭秘Scroller...

1. 你应该知道的scrollTo()和scrollBy()

首先看一下这两个方法的源码:

   /**
     * Move the scrolled position of your view. This will cause a call to
     * {@link #onScrollChanged(int, int, int, int)} and the view will be
     * invalidated.
     * @param x the amount of pixels to scroll by horizontally
     * @param y the amount of pixels to scroll by vertically
     */
    public void scrollBy(int x, int y) {
        scrollTo(mScrollX + x, mScrollY + y);
    }
   /**
     * Set the scrolled position of your view. This will cause a call to
     * {@link #onScrollChanged(int, int, int, int)} and the view will be
     * invalidated.
     * @param x the x position to scroll to
     * @param y the y position to scroll to
     */
    public void scrollTo(int x, int y) {
        if (mScrollX != x || mScrollY != y) {
            int oldX = mScrollX;
            int oldY = mScrollY;
            mScrollX = x;
            mScrollY = y;
            invalidateParentCaches();
            onScrollChanged(mScrollX, mScrollY, oldX, oldY);
            if (!awakenScrollBars()) {
                postInvalidateOnAnimation();
            }
        }
    }

两个方法第一个参数x表示相对于当前位置横向移动的距离,正值向左移动,负值向右移动。第二个参数y表示相对于当前位置纵向移动的距离,正值向上移动,负值向下移动,单位都是像素。
不同的是,scrollTo()方法让View相对于初始的位置滚动某段距离,scrollBy()方法则是让View相对于当前的位置滚动某段距离。同时可以发现scrollBy()是通过scrollTo()方法实现的。

上边我们说过,两个方法第一个参数x表示相对于当前位置横向移动的距离,正值向左移动,负值向右移动。这是为什么呢???看下scrollTo()方法中的mScrollX = x;一行,我们传入的x值被赋值给了mScrollX ,mScroller又是什么东东呢?其实View有一个getScrollX(),继续走进源码的世界:

public final int getScrollX() {
        return mScrollX;
    }

可以看到通过getScrollX()方法可以返回mScrollX,也就是View在 水平方向移动的距离,其实关于mScrollX的值有个变化规律:mScrollX等于View左边缘和View内容左边缘的水平方向x坐标的差值,同样的道理mScrollY 等于View上边缘和View内容上边缘的垂直方向y坐标的差值

通过scrollTo()或scrollBy()实现View的移动,只是View的内容的移动,View本身的位置并不会发生改变。此时的View可以理解为一个ViewGroup,而内容可以理解成ViewGroup中的子View,假设View的坐标原点为(0, 0),如果View内容从左向右移动,则View的内容左边缘x值将大于0,而View的左边缘x值始终为0,所以View左边缘和View内容左边缘的水平方向x坐标的差值小于0,也就是mScrollX的值小于0,这也就解释了为什么当scrollTo()或scrollBy()的x参数为负值,看起来View是向右移动的,这样也就可以理解x参数为正值的情况了,同理纵向上的y参数值也是一个道理。

说了这么多scroll相关的方法,也该聊聊我们的Scroller了...

2. Scroller弹性滑动原理

首先看一下Scroller的典型用法:

Scroller mScroller = new Scroller(mContext);

private void smoothScroll(int destX, int destY) {
        int scrollX = getScrollX();
        int deltaX = destX - scrollX;
        mScroller.startScroll(scrollX, 0, deltaX, 0, 500);
        invalidate();
    }

    @Override
    public void computeScroll() {
        super.computeScroll();
        if (mScroller.computeScrollOffset()) {
            scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
            invalidate();
        }
    }

目测startScroll()方法和View的滑动有关,继续看源码:

public void startScroll(int startX, int startY, int dx, int dy, int duration) {
        mMode = SCROLL_MODE;
        mFinished = false;
        mDuration = duration;
        mStartTime = AnimationUtils.currentAnimationTimeMillis();
        mStartX = startX;
        mStartY = startY;
        mFinalX = startX + dx;
        mFinalY = startY + dy;
        mDeltaX = dx;
        mDeltaY = dy;
        mDurationReciprocal = 1.0f / (float) mDuration;
    }

原来startScroll()方法只是进行相关参数的初始化,其中startX、startY代表滑动的起点,dx、dy代表需要滑动的距离,duration代表整个滑动需要的时间。骗子...这里并没有View滑动的相关逻辑。那么View如何通过Scroller实现弹性滑动呢?其实是通过startScroll()下面的invalidate();方法,略抽象,可能我们更多的知道invalidate()方法能够使得View重绘,其实奥秘就在这里,通过使View重绘,会间接的执行computeScroll()方法,继续看源码:

public void computeScroll() {
    }

原来是一个空方法,所以需要我们自行实现,so sad...
而在computeScroll()中,我们首先通过computeScrollOffset()方法判断滑动是否结束,这个方法如何工作呢?继续看源码:

public boolean computeScrollOffset() {
        if (mFinished) {
            return false;
        }

        int timePassed = (int)(AnimationUtils.currentAnimationTimeMillis() - mStartTime);
    
        if (timePassed < mDuration) {
            switch (mMode) {
            case SCROLL_MODE:
                final float x = mInterpolator.getInterpolation(timePassed * mDurationReciprocal);
                mCurrX = mStartX + Math.round(x * mDeltaX);
                mCurrY = mStartY + Math.round(x * mDeltaY);
                break;
            case FLING_MODE:
                final float t = (float) timePassed / mDuration;
                final int index = (int) (NB_SAMPLES * t);
                float distanceCoef = 1.f;
                float velocityCoef = 0.f;
                if (index < NB_SAMPLES) {
                    final float t_inf = (float) index / NB_SAMPLES;
                    final float t_sup = (float) (index + 1) / NB_SAMPLES;
                    final float d_inf = SPLINE_POSITION[index];
                    final float d_sup = SPLINE_POSITION[index + 1];
                    velocityCoef = (d_sup - d_inf) / (t_sup - t_inf);
                    distanceCoef = d_inf + (t - t_inf) * velocityCoef;
                }

                mCurrVelocity = velocityCoef * mDistance / mDuration * 1000.0f;
                
                mCurrX = mStartX + Math.round(distanceCoef * (mFinalX - mStartX));
                // Pin to mMinX <= mCurrX <= mMaxX
                mCurrX = Math.min(mCurrX, mMaxX);
                mCurrX = Math.max(mCurrX, mMinX);
                
                mCurrY = mStartY + Math.round(distanceCoef * (mFinalY - mStartY));
                // Pin to mMinY <= mCurrY <= mMaxY
                mCurrY = Math.min(mCurrY, mMaxY);
                mCurrY = Math.max(mCurrY, mMinY);

                if (mCurrX == mFinalX && mCurrY == mFinalY) {
                    mFinished = true;
                }

                break;
            }
        } else {
            mCurrX = mFinalX;
            mCurrY = mFinalY;
            mFinished = true;
        }
        return true;
    }

看第一个if条件,如果滑动动画已经finish,则返回fasle,继续看第二个if条件,如果执行滑动动画已经花费的时间小于整个滑动动画需要的时间,则计算出mCurrXmCurrY的值,也就是当前View内容左边缘的x、y坐标的值,同时返回true,所以,如果滑动的动画未结束则返回true否则返回false。当返回true时,则调用scrollTo(mScroller.getCurrX(), mScroller.getCurrY())进行View的滑动,其中参数mScroller.getCurrX()、mScroller.getCurrY()得到的就是上边的mCurrX和mCurrY,scrollTo之后继续执行invalidate()方法,此时又会导致View重绘,又一次执行computeScroll()方法...,这样的反复执行,直到computeScrollOffset()方法返回flase,即完成View的滑动。

通过上边的分析可以看出,仅仅通过Scroller,并不能实现View的滑动效果,同时需要配合View的invalidate()、computeScroll()、scrollTo()方法才可以完成。

说了这么多的理论,也该实践一下了...

继续阅读Android Scroller实现View弹性滑动完全解析