1.Bitmap介绍android
Bitmap实如今android.graphics包中。可是Bitmap类的构造函数是私有的,外面并不能实例化,只能是经过JNI实例化。算法
这必然是某个辅助类提供了建立Bitmap的接口,而这个类的实现经过JNI接口来实例化Bitmap的,这个类就是BitmapFactory。编程
Bitmap是一片连续的内存空间,而咱们的应用程序的内存是散乱的,若是当零散的空间下容纳不了bitmap时就致使OOM。数组
2.BitmapFactory缓存
其中提供的一个Opitions属性能够用来提早获取到图片的宽高信息,下面会介绍。网络
2.Bitmap优化app
当前有一张图片,大小仅为1M,可是其规格为3648*2736,如今须要加载此图片总像素数=3648*2736=9980928jsp
假设如今像素采用ARGB_4444标准,则其占用的总空间为:
图片占用空间=总像素数 *像素的单位 ----> 9980928 * 2bytes=19961856bytes = 19M > 16M OOM内存溢出ide
因此咱们必须对图片进行缩放才能加载:函数
假设:
图片的宽和高: 3648 * 2736 屏幕的宽和高: 320 * 480
计算缩放比:
宽度缩放比例: 3648 / 320 = 11 高度缩放比例: 2736 / 480 = 5
比较宽和高的缩放比例, 哪个大用哪个进行缩放
缩放后的图片:
3648 / 11 = 331
2736 / 11 = 248
缩放后图片的宽和高: 331* 248
331* 248=882088 * 2bytes=160K
3.解析图片的三种方式
1.使用BitmapFactory解析图片
2.使用BitmapDrawable解析图片
3.使用InputStream和BitmapDrawable绘制
图片的特效包括,图形的缩放、镜面、倒影、旋转、位移等。图片的特效是将原图的图形矩阵乘以一个特效矩阵,造成一个新的图形矩阵来实现的。
Matrix维护了一个3*3的矩阵去更改像素点的坐标。
图形的默认矩阵用数组表示为:
{ 1, 0, 0, 表示向量x = 1x + 0y + 0z
0, 1, 0, 表示向量y = 0x + 1y + 0z
0, 0, 1 } 表示向量z = 0x + 0y + 1z
经过更改图形矩阵的值,能够作出缩放/镜面/倒影等图片特效。
1.缩放
2.镜面
惟一不一样的地方在矩阵处
3.倒影
4.旋转
5.位移
Android提供了颜色过滤器来进行颜色处理。
a)ColorMatrixColorFilter:经过使用一个4*5的颜色矩阵来建立一个颜色过滤器,改变图片的颜色信息。
b) 图形颜色默认矩阵是一个4x5的矩阵, 数组表现为:
{1, 0, 0, 0, 0, // red 1*R + 0*G + 0*B + 0*A + 0
0, 1, 0, 0, 0, // green 0*R + 1*G + 0*B + 0*A + 0
0, 0, 1, 0, 0, // blue 0*R + 0*G + 1*B + 0*A + 0
0, 0, 0, 1, 0} // alpha 0*R + 0*G + 0*B + 1*A + 0
颜色矩阵的每一行的最后一个值更改时,其对应的颜色值就会发生改变,因此更改颜色只需修改其对应颜色矩阵行的最后一项的值便可,最大值范围为255
a)首先咱们声明一个初始化矩阵数组和颜色过滤器
b)重写progressBar的onProgressChanged方法,并修改矩阵数组的值后,从新建立新的颜色过滤器。
a)首先在布局文件建立一个ImageView,让其宽高都填充父窗体,并在代码中进行实例化,并设置它的Touch事件。
mIv = (ImageView) findViewById(R.id.iv); mIv.setOnTouchListener(this);
b) 而后重写onTouch(View v, MotionEvent event)方法对手势进行操做
@Override public boolean onTouch(View v, MotionEvent event) { int action = event.getAction(); switch (action) { case MotionEvent.ACTION_DOWN: mStartX = (int) event.getX(); mStartY = (int) event.getY(); if(mBitmap == null){ mBitmap = Bitmap.createBitmap(mIv.getWidth(),mIv.getHeight(),Config.ARGB_8888); // 画布 mCanvas = new Canvas(mBitmap); // 设置背景色白色 mCanvas.drawColor(Color.WHITE); mPaint = new Paint(); // 设置画笔颜色为红色,线条粗细为5 mPaint.setColor(Color.RED); mPaint.setStrokeWidth(5); } break; case MotionEvent.ACTION_MOVE: // 记录移动到的位置坐标 int moveX = (int) event.getX(); int moveY = (int) event.getY(); // 绘制线条,链接起始位置和当前位置 mCanvas.drawLine(mStartX, mStartY, moveX, moveY, mPaint); // 设置显示 mIv.setImageBitmap(mBitmap); // 将起始位置改成为当前移动到的位置 mStartX = moveX; mStartY = moveY; break; case MotionEvent.ACTION_UP: break; } return true; }
c) 上述基本功能已经完成,咱们还须要一个清空的方法
// 清除界面 public void clear(View view){ mBitmap = null; mIv.setImageBitmap(null); }
1. 简介
如今android应用中不可避免的要使用图片,有些图片是能够变化的,须要每次启动时从网络拉取。
如今有一个问题:假如每次启动的时候都从网络拉取图片的话,势必会消耗不少流量。在当前的情况下,对于非wifi用户来讲,流量仍是很贵的,
一个很耗流量的应用,其用户数量级确定要受到影响。固然,我想,向百度美拍这样的应用,必然也有其内部的图片缓存策略。总之,图片缓存是很重要并且是必须的。
2.图片缓存的原理
实现图片缓存也不难,须要有相应的cache策略。这里我采用 内存-文件-网络 三层cache机制,其中内存缓存包括强引用缓存和软引用缓存(SoftReference),
其实网络不算cache,这里姑且也把它划到缓存的层次结构中。当根据url向网络拉取图片的时候,先从内存中找,若是内存中没有,再从缓存文件中查找,
若是缓存文件中也没有,再从网络上经过http请求拉取图片。在键值对(key-value)中,这个图片缓存的key是图片url的hash值,value就是bitmap。因此,
按照这个逻辑,只要一个url被下载过,其图片就被缓存起来了。
关于Java中对象的软引用(SoftReference),若是一个对象具备软引用,内存空间足够,垃圾回收器就不会回收它;若是内存空间不足了,就会回收这些对象的内存。
只要垃圾回收器没有回收它,该对象就能够被程序使用。软引用可用来实现内存敏感的高 速缓存。使用软引用能防止内存泄露,加强程序的健壮性。
固然,这种回收机制只存在于Android 2.3版本以前,Android 2.3以后垃圾回收器会更倾向于回收软引用、弱引用,它们变得再也不可靠。
从代码上来讲,采用一个ImageManager来负责图片的管理和缓存,函数接口为public void loadBitmap(String url, Handler handler) ;其中url为要下载的图片地址,
handler为图片下载成功后的回调,在handler中处理message,而message中包含了图片的信息以及bitmap对象。ImageManager中使用的ImageMemoryCache(内存缓存)、
ImageFileCache(文件缓存)以及LruCache(最近最久未使用缓存)会在后续文章中介绍。
一、MediaPlayer
该播放器同时只能播放一个音乐文件,文件大小并无限制。MediaPlayer必须严格按照状态图操做,不然就会出现错误。
MediaPlayer播放的实例:
// 建立MediaPlayer对象 MediaPlayer mediaPlayer = new MediaPlayer(); mediaPlayer.setDataSource(Environment.getExternalStorageDirectory() + "/bg.mp3"); // 准备 mediaPlayer.prepare(); // 判断当前音乐是否在播放 boolean isPlaying = mediaPlayer.isPlaying(); if(!isPlaying){ mediaPlayer.start(); }else{ Toast.makeText(this,"音乐正在播放中",0).show(); }
二、SoundPool
SoundPool特色是能够自行设置声音的品质、音量、播放比率等参数。而且它能够同时管理多个音频流,每一个流都有独自的ID,对某个音频流的管理都是经过ID进行的。
a)SoundPool最大只能申请1M的内存空间,只能用一些很短的声音片断,而不是用它来播放歌曲或者作游戏背景音乐。
b)SoundPool提供了pause和stop方法,但这些方法建议最好不要轻易使用,由于有些时候它们可能会使你的程序莫名其妙的终止。
c)SoundPool的效率问题。其实SoundPool的效率在这些播放类中算是很好的,这可能会影响用户体验。
SoundPool播放的实例:
// 建立SoundPool对象 SoundPool soundPool = new SoundPool(1,AudioManager.STREAM_MUSIC,1); // 加载SoundPool音乐文件 int soundID = soundPool.load(this,R.raw.shoot1,1); // 播放soundPool音乐 soundPool.play(soundID,1,1,0,0,1);
1.第一种
a)第一种方式能够经过surfaceView和MediaPlayer来实现该功能,首先建立一个SufaceView实例
2.第二种
a)设置布局文件,布局文件比较简单,所以这里只给你VideoView标签。
mSurface = (SurfaceView) findViewById(R.id.sfv); mHolder = mSurface.getHolder(); mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
b) 而后经过以下代码播放,注意视频知支持mp4格式
/ 建立播放器对象 MediaPlayer player = new MediaPlayer(); // 获取音乐路径 String path = "/mnt/sdcard/oppo.mp4"; // 给播放器设置音乐路径 player.setDataSource(path); // 设置音乐格式 player.setAudioStreamType(AudioManager.STREAM_MUSIC); // 设置输出画面 player.setDisplay(mHolder); // 准备 player.prepare(); // 播放 player.start();
2.第二种
a)设置布局文件,布局文件比较简单,所以这里只给你VideoView标签。
<VideoView android:id="@+id/video" android:layout_width="match_parent" android:layout_height="match_parent"/>
b) 设置VideoView的播放文件路径和媒体控制器,调用start方法便可播放媒体文件,固然如今只是让当前类实现了MediaController接口
VideoView video = (VideoView) findViewById(R.id.video); video.setVideoPath("/mnt/sdcard/oppo.mp4"); video.setMediaController(new MediaController(this)); // 开始播放 video.start(); // 设置当前播放器窗口为焦点 video.requestFocus();
c)这时,视频已经能够播放,若是想实现本身的业务逻辑,能够定义类实现MediaPlayerControl接口,其中提供大量的回调方法供咱们使用。
Android手机中内置了不少传感器,其主要类型有:方向、加速度(重力)、光线、磁场、距离(临近性)、温度等。
1.传感器的使用
a) 获取传感器管理器SensorManager
SensorManager sm = (SensorManager) getSystemService(SENSOR_SERVICE);
b) 经过传感器管理器对象得到指定类型的传感器:
// 获取指定传感器对象,获取系统默认的重力加速度传感器 Sensor sensor = sm.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
c) 经过传感器管理器对象得到手机中全部的传感器
// 获取手机支持的全部传感器 List<Sensor> sensorList = sm.getSensorList(Sensor.TYPE_ALL); for (int i = 0; i < sensorList.size(); i++) { Sensor sensor = sensorList.get(i); // 获取传感器名称 String name = sensor.getName(); // 获取传感器厂商 String vendor = sensor.getVendor(); // 获取传感器版本号 int version = sensor.getVersion(); }
d) 使用传感器管理器对象注册传感器来使一个传感器工做:
sm.registerListener(new SensorEventListener() { // 当传感器改变时触发该函数 @Override public void onSensorChanged(SensorEvent event) { } // 当传感器精度更改时触发该函数 @Override public void onAccuracyChanged(Sensor sensor, int accuracy) { } },sensor,SensorManager.SENSOR_DELAY_NORMAL);
(1) listener :传感器事件监听器
(2) sensor :要被注册的传感器对象
(3) rate :采样率,分为最快、游戏、普通、用户界面几种当应用程序请求特定的采样率时,其实只是对传感器子系统的一个建议,不保证特定的采样率可用。
采样率的四种类型详解:
最快: SensorManager.SENSOR_DELAY_FASTEST,最低延迟,通常不是特别敏感的处理不推荐使用,该种模式可能形成耗电,因为传递的为原始数据,
算法不处理好会影响游戏逻辑和UI的性能
游戏: SensorManager.SENSOR_DELAY_GAME,游戏延迟,通常绝大多数的实时性较高的游戏都使用该级别
普通: SensorManager.SENSOR_DELAY_NORMAL,标准延迟,对于通常的益智类或EASY级别的游戏可使用,但太低的采样率可能对一些赛车类游戏有跳帧现象
用户界面: SensorManager.SENSOR_DELAY_UI,通常对于屏幕方向自动旋转使用,相对节省电能和逻辑处理,通常游戏开发中咱们不使用
总结:因为传感器比较多,这里只是简单的概述,后续会花大量的时间整理传感器的使用。
调用系统摄像头进行拍照和摄像无需添加权限,直接调用便可。只需知道系统摄像头的action和category就能够调用系统摄像头。
a)打开Android源码,查看”\packages\apps\”文件文件目录下的Camera应用,即系统摄像头的应用程序。打开其清单文件文件,查看其Activity的action和category信息。
b)Camera类的action和category
<intent-filter> <action android:name="android.media.action.IMAGE_CAPTURE" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter>
VideoCamera类的action和category
<intent-filter> <action android:name="android.media.action.VIDEO_CAMERA" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter>
c) 已知调用系统摄像头拍照和摄像功能对应的action和category信息,采用隐式调用的方式调用Activity。
因为但愿在调用拍照或摄像功能后回到当前应用的界面,且得知拍照或摄像的结果如何是否成功使用startActivityForResult方法开启Activity,并重写onActivityResult方法处理回传的数据。
1.拍照功能
Intent intent = new Intent(); // 设置Action intent.setAction("android.media.action.IMAGE_CAPTURE"); // 建立一个文件 File file = new File(Environment.getExternalStorageDirectory().getAbsolutePath(),"my.jsp"); // 建立Uri对象 Uri uri = Uri.fromFile(file); // 设置图片输出路径 intent.putExtra(MediaStore.EXTRA_OUTPUT,uri); // 开启Activity startActivityForResult(intent,100);
2.摄像功能
Intent intent = new Intent(); intent.setAction("android.media.action.VIDEO_CAPTURE"); File file = new File(Environment.getExternalStorageDirectory().getAbsolutePath(),"myVedio.mp4"); Uri uri = Uri.fromFile(file); intent.putExtra(MediaStore.EXTRA_OUTPUT,uri); startActivityForResult(intent,101);
3.自定义拍照
a)首先定义一个SurfaceView用做相机的预览
SurfaceView mSurfaceView = (SurfaceView) this.findViewById(R.id.surfaceView); SurfaceHolder holder = mSurfaceView.getHolder(); holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); holder.setKeepScreenOn(true); holder.addCallback(new SurfaceCallback());
b )建立SurfaceCallback来开启预览,其中提供了三个回调方法,方便咱们对相机预览进行管理。
/**sufaceView的回调*/ private final class SurfaceCallback implements Callback { @Override public void surfaceCreated(SurfaceHolder holder) { try { mCamera = Camera.open(0);// 0:打开后置 1:打开前置 if(mCamera != null){ mCamera.setPreviewDisplay(holder); mCamera.setDisplayOrientation(getPreviewDegree(NewCameraCheck.this)); mCamera.startPreview(); }else{ Toast.makeText(NewCameraCheck.this, "获取不到相机实例", Toast.LENGTH_SHORT).show(); } } catch (Exception e) { e.printStackTrace(); } } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width,int height) { if(mCamera != null){ parameters = mCamera.getParameters(); parameters.setPictureFormat(PixelFormat.JPEG); parameters.setPreviewSize(width, height); parameters.setPreviewFrameRate(5); parameters.setPictureSize(width, height); parameters.setJpegQuality(80); }else{ Toast.makeText(NewCameraCheck.this, "获取不到相机实例,没法设置参数。", Toast.LENGTH_SHORT).show(); } } // 在Surface销毁时释放资源 @Override public void surfaceDestroyed(SurfaceHolder holder) { if(mCamera != null){ //mCamera.stopPreview(); mCamera.release(); mCamera = null; } } }
c) 提供一个静态方法用来对相机预览的旋转角度进行自动调整
// 提供一个静态方法,用于根据手机方向得到相机预览画面旋转的角度 public int getPreviewDegree(Activity activity) { // 得到手机的方向 int rotation = activity.getWindowManager().getDefaultDisplay().getRotation(); mDegree = 0; // 根据手机的方向计算相机预览画面应该选择的角度 switch (rotation) { case Surface.ROTATION_0: mDegree = 90; break; case Surface.ROTATION_90: mDegree = 0; break; case Surface.ROTATION_180: mDegree = 270; break; case Surface.ROTATION_270: mDegree = 180; break; } return mDegree; }
d) 咱们能够经过调用Camera的tackPicture()方法进行拍照,若是咱们须要对焦拍照的话,使用下面这段代码拍照
public void tackImage(){ if(mCamera == null){ Toast.makeText(NewCameraCheck.this, "获取不到相机实例,拍照失败!", Toast.LENGTH_SHORT).show(); return; } mCamera.autoFocus(new AutoFocusCallback() { @Override public void onAutoFocus(boolean success, Camera camera) { mCamera.takePicture(null, null, new MyPictureCallback()); //mCamera.startPreview(); } }); }
e) 除此以外,咱们相机适配须要注意清单文件中的配置,固然还要记得添加权限
清单文件配置: <activity android:name="com.hll.phoneuser.activity.NewCameraCheck" android:screenOrientation="portrait" android:theme="@android:style/Theme.Light.NoTitleBar.Fullscreen" /> 权限配置: <uses-permission android:name="android.permission.CAMERA" /> <uses-feature android:name="android.hardware.camera" /> <uses-feature android:name="android.hardware.camera.autofocus" />
4.自定义摄像
使用Camera+MediaRecoder + SurfaceView控件可实现录制视频的功能。
a)首先定义一个SurfaceView用做相机的预览
setContentView(R.layout.activity_main); SurfaceView mSurfaceView = (SurfaceView) findViewById(R.id.surface); SurfaceHolder holder = mSurfaceView .getHolder(); holder.addCallback(new MyVideoCallback());
b) 建立SurfaceCallback来开启预览,其中提供了三个回调方法,方便咱们对相机预览进行管理。
class MyVideoCallback implements Callback{ @Override public void surfaceCreated(SurfaceHolder holder) { mCamera.open(); try { mCamera.setPreviewDisplay(holder); mCamera.startPreview(); } catch (IOException e) { e.printStackTrace(); } } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { } @Override public void surfaceDestroyed(SurfaceHolder holder) { mCamera.stopPreview(); mCamera.release(); } }
c)开始录像须要设置大量的参数,代码中异常未处理,具体操做以下
// 中止预览 mCamera.stopPreview(); // 解锁摄像头 mCamera.unlock(); // 初始化MediaRecorder对象 MediaRecorder recorder = new MediaRecorder(); // 给recorder设置摄像头 recorder.setCamera(mCamera); // 设置音频源 recorder.setAudioSource(AudioSource.CAMCORDER); // 设置视频源 recorder.setVideoSource(VideoSource.CAMERA); // 设置录像质量参数 CamcorderProfile profile = CamcorderProfile.get(CamcorderProfile.QUALITY_480P); recorder.setProfile(profile); // 设置录像输出路径 recorder.setOutputFile(Environment.getExternalStorageDirectory().getAbsolutePath()+"/test.mp4"); // 设置预览显示对象 recorder.setPreviewDisplay(mHolder.getSurface()); // 准备 recorder.prepare(); recorder.start();
d) 若是想中止录像的话,只须要调用以下代码:
mRecorder.stop();
mRecorder.reset();
mRecorder.release();
mCamera.lock();
mCamera.startPreview();
e) 固然别忘记添加权限
<uses-permission android:name="android.permission.CAMERA"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.RECORD_AUDIO"/>
后续进阶能够参看:Android多媒体开发高级编程
首先调用系统的相机或打开相册来获取图片
// 拍照 btnCamera.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(new File( Environment.getExternalStorageDirectory(), "temp.jpg"))); startActivityForResult(intent, PHOTOHRAPH); } }); // 从相册选择 btnPhoto.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(); intent.setAction(Intent.ACTION_PICK); intent.setType("image/*"); startActivityForResult(intent, PHOTOZOOM); } });
在activity中的onActivityResult中进行回调获取到打开的图片
public static final int NONE = 0; public static final int PHOTOHRAPH = 1;// 拍照 public static final int PHOTOZOOM = 2; // 缩放 public static final int PHOTORESULE = 3;// 结果 @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { switch (requestCode) { case NONE: return; case PHOTOHRAPH: // 拍照 // 设置文件保存路径这里放在根目录下 File picture = new File(Environment.getExternalStorageDirectory() + "/temp.jpg"); startPhotoZoom(Uri.fromFile(picture)); break; case PHOTOZOOM: // 从相册选择 startPhotoZoom(data.getData()); break; case PHOTORESULE: Bundle extras = data.getExtras(); if (extras != null) { Bitmap photo = extras.getParcelable("data"); ByteArrayOutputStream stream = new ByteArrayOutputStream(); photo.compress(Bitmap.CompressFormat.JPEG, 75, stream);// (0 - 100)压缩文件 // 设置头像显示 mIvPhoto.setImageBitmap(photo); dialog.dismiss(); } break; } }
其中封装的方法startPhotoZoom,是对图片进行宽高和压缩等处理:
public void startPhotoZoom(Uri uri) { Intent intent = new Intent("com.android.camera.action.CROP"); intent.setDataAndType(uri, "image/*"); intent.putExtra("crop", "true"); // aspectX aspectY 是宽高的比例 intent.putExtra("aspectX", 1); intent.putExtra("aspectY", 1); // outputX outputY 是裁剪图片宽高 intent.putExtra("outputX", 64); intent.putExtra("outputY", 64); intent.putExtra("return-data", true); startActivityForResult(intent, PHOTORESULE); }