05Android學習從零單排之AndroidMultimedia(多媒體)
讀了那麼多年的書讓我明白一個道理。人要穩重,不要想到啥就做啥。做一行越久即使你不會,幾年之後慢慢的你也會了,加上一點努力你或許你能成爲別人眼中的專家。
注:本章blog主要學習Android下圖片、音頻、視頻的處理。
Android下大圖片的處理
在平時開發中,如果我們加載一個圖片分辨率或者加載批量圖片,超過了手機的屏幕分辨率或者內存空間時,就會導致加載圖片不成功,並會報OOM異常,所以爲了解決這個問題,我們就需要通過一定比例的縮放,方便圖片加載。
大圖片縮放的步驟
- 1、獲取當前手機屏幕的寬和高(分辨率)。
- 2、獲取圖片的寬和高。
- 3、用圖片的分辨率除以當前屏幕的分辨率。
- 4、加載縮放後的圖片資源。
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ImageView imageView = (ImageView) findViewById(R.id.iv);
// Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.dog);//執行到這裏就是OOM了
// imageView.setImageBitmap(bitmap);
//獲取當前屏幕的寬高
WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
Point point = new Point();
//TODO 注意測量屏幕的寬高和下面測量圖片的寬高,這種寫法,以前沒遇到過。
/**
* 基本順序是 wm.getDefaultDisplay().getSize發現需要Point對象,就去new了
* 但是new完之後,Point不能直接使用,例如,不能直接point.x。
* 需要被wm裝載到容器之後,才能執行point.x。不然獲取不到屏幕的寬高。
*/
wm.getDefaultDisplay().getSize(point);
int x = point.x;
int y = point.y;
Toast.makeText(this, "當前屏幕分辨率"+x+"*"+y, Toast.LENGTH_SHORT).show();
BitmapFactory.Options options = new BitmapFactory.Options();
//通過bitmap獲取的資源圖片的信息,而不是加載圖片資源。
options.inJustDecodeBounds = true;
//Options
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.dog,options);
//獲取圖片的寬高,方法同上
int outWidth = options.outWidth;
int outHeight = options.outHeight;
Toast.makeText(this, "獲取到圖片的分辨率:"+outWidth+"*"+outHeight, Toast.LENGTH_SHORT).show();
//計算縮放比例
int scale = 1;
int scaleX = outWidth / x;
int scaleY = outHeight / y;
/**
* scaleX>=scleY 是判斷寬高比例
* scaleX>scale 是判斷圖片像素除以屏幕像素是否大於1,也就是不能比屏幕小才做縮放。
*/
if (scaleX>=scaleY&&scaleX>scale) {
scale = scaleX;
}else if (scaleY >= scaleX && scaleY>scale) {
scale = scaleY;
}
//inSampleSize 可以把它看成setSampleSize,就是設置縮放後的圖片大小。
options.inSampleSize = scale;
//開始真正加載圖片資源了,不是獲取圖片信息了。
options.inJustDecodeBounds = false;
Bitmap bit = BitmapFactory.decodeResource(getResources(), R.drawable.dog,options);
int scaleWidth = options.outWidth;
int scaleHeight = options.outHeight;
Toast.makeText(this, "縮放後的圖片分辨率:"+scaleWidth+"*"+scaleHeight, Toast.LENGTH_SHORT).show();
imageView.setImageBitmap(bit);
}
Android對圖片的修改操作
Android是不允許對圖片的源文件進行直接修改的,必須要先複製改圖片,才能對圖片進行修改或塗鴉。
創建一個原圖的副本(即複製圖片)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
btnRotate.setOnClickListener(this);
btnMove.setOnClickListener(this);
btnOverturn.setOnClickListener(this);
btnReflection.setOnClickListener(this);
resource = BitmapFactory.decodeResource(getResources(), R.drawable.meinv);
iv1.setImageBitmap(resource);
//給第二個ImageView設置和第一個一樣的圖片
bitmap = Bitmap.createBitmap(resource.getWidth()*2, resource.getHeight()*2, resource.getConfig());
//準備畫布
canvas = new Canvas(bitmap);
//開始作畫,參照第一張圖片 Paint可以理解爲筆
matrix = new Matrix();
// matrix.setRotate(30, bitmap.getWidth()/2, bitmap.getHeight()/2);
canvas.drawBitmap(resource, matrix, new Paint());
iv2.setImageBitmap(bitmap);
}
對創建的圖片進行“旋轉”、“移動”等操作。
- 1、旋轉
matrix.setRotate(30); - 2、平移
matrix.setTranslate(20, 0); - 3、縮放
matrix.setScale(0.5f, 0.5f); - 4、倒影
//對圖片進行倒影效果
matrix.setScale(1.0f, -1.0f);
matrix.postTranslate(0, copyBitmap.getHeight()); - 5、翻轉
//對圖片進行鏡面
matrix.setScale(-1.0f, 1.0f);
matrix.postTranslate(copyBitmap.getWidth(), 0);
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_rotate:
matrix.setRotate(30,bitmap.getWidth()/2,bitmap.getHeight()/2);
canvas.drawBitmap(resource, matrix, new Paint());
iv2.setImageBitmap(bitmap);
break;
case R.id.btn_move:
matrix.setTranslate(20,20);
canvas.drawBitmap(resource, matrix, new Paint());
iv2.setImageBitmap(bitmap);
break;
case R.id.btn_overturn:
//對圖片進行鏡面
matrix.setScale(-1.0f, 1.0f);
matrix.postTranslate(bitmap.getWidth(), 0);
canvas.drawBitmap(resource, matrix, new Paint());
iv2.setImageBitmap(bitmap);
break;
case R.id.btn_reflection:
//對圖片進行倒影效果
matrix.setScale(1.0f, -1.0f);
matrix.postTranslate(0, bitmap.getHeight());
canvas.drawBitmap(resource, matrix, new Paint());
iv2.setImageBitmap(bitmap);
break;
}
Android版塗鴉板
通過Bitmap複製一張圖片作爲背景,然後獲取到觸摸事件,不斷的在這張圖上作畫。
最後發送一條廣播,通知圖庫更新。
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
iv = (ImageView) findViewById(R.id.iv);
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.bb);
// iv.setImageBitmap(bitmap);
//複製了一直帶有屬性的畫板
newBitmap = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), bitmap.getConfig());
//準備畫布
final Canvas canvas = new Canvas(newBitmap);
final Paint paint = new Paint();
canvas.drawBitmap(bitmap,new Matrix(),paint);
//在畫布上畫一條線
// canvas.drawLine(10,10,200,100,paint);
iv.setOnTouchListener(new View.OnTouchListener() {
int downX = 0;
int downY = 0;
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
downX = (int) event.getX();
downY = (int) event.getY();
// Toast.makeText(MainActivity.this, "當前按下的座標X:"+downX+"Y:"+downY, Toast.LENGTH_SHORT).show();
break;
case MotionEvent.ACTION_MOVE:
int moveX = (int) event.getX();
int moveY = (int) event.getY();
paint.setStrokeWidth(3);
// System.out.println("當前移動座標X:" + moveX + "Y:" + moveY);
canvas.drawLine(downX,downY,moveX,moveY,paint);
iv.setImageBitmap(newBitmap);
//重新計算座標
downX = (int) event.getX();
downY = (int) event.getY();
break;
case MotionEvent.ACTION_UP:
break;
}
return true;
}
});
iv.setImageBitmap(newBitmap);
}
public void saveImage(View view){
String path = Environment.getExternalStorageDirectory().getPath() + "/Download/" + "哈哈.png";
// String path = getFilesDir().getPath() + "/哈哈.png";
FileOutputStream fos = null;
try {
fos = new FileOutputStream(path);
boolean compress = newBitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);
if (compress) {
//發送一條廣播,更新圖庫(已失效。)
// Intent intent = new Intent();
// intent.setAction(Intent.ACTION_MEDIA_MOUNTED);
// intent.setData(Uri.parse("file://"+getFilesDir()));
// sendBroadcast(intent);
//發送廣播,通知圖庫更新(掃描文件)。
sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.parse("file://" + Environment.getExternalStorageDirectory().getPath() + "/Download/")));
fos.close();
Toast.makeText(this, "塗鴉保存成功", Toast.LENGTH_SHORT).show();
}else {
Toast.makeText(this, "保存失敗", Toast.LENGTH_SHORT).show();
}
} catch (Exception e) {
e.printStackTrace();
}
}
Android音頻播放
相對來說Android下的音視頻播放還是比較簡單的,都被封裝好在MediaPlay裏面了,簡單的Demo,幾行代碼就可以了。
public void startOpen(View view){
//初始化MediaPlay,根據官網copy一份,改下path就行了
MediaPlayer mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
try {
mediaPlayer.setDataSource("http://10.0.2.2/xpg.mp3");
mediaPlayer.prepare();
mediaPlayer.start();
} catch (IOException e) {
e.printStackTrace();
}
}
複雜點的,帶服務後臺播放加顯示播放進度的Mp3播放器
- 混合方式開啓音樂服務(就是爲了調用服務裏面的方法)
public class MainActivity extends AppCompatActivity {
private static final String TAG = "Hsia";
private Iservices iservices;
private static SeekBar mSB;
private MyConn myConn;
public static Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Bundle data = msg.getData();
//總大小
duration = data.getInt("duration");
//當前位置
currentPosition = data.getInt("currentPosition");
mSB.setMax(duration);
mSB.setProgress(currentPosition);
}
};
private static int currentPosition;
private static int duration;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.d(TAG, "onCreate: ");
mSB = ((SeekBar) findViewById(R.id.sb));
// mSB.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
// //當SeekBar進度改變時
// @Override
// public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
// int position = seekBar.getProgress(); //獲取當前播放的進度
// //調用服務裏面的方法更新更新到制定位置
// iservices.callsetMusicProgress(position);
// }
//
// @Override
// public void onStartTrackingTouch(SeekBar seekBar) {
//
// }
//
// @Override
// public void onStopTrackingTouch(SeekBar seekBar) {
//
// }
// });
mSB.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
//停止拖動回調
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
int position = seekBar.getProgress(); //獲取當前播放的進度
iservices.callsetMusicProgress(position);
}
//開始拖動
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
//進度發生了改變
@Override
public void onProgressChanged(SeekBar seekBar, int progress,
boolean fromUser) {
}
});
Intent intent = new Intent(this,MusicServer.class);
startService(intent);
myConn = new MyConn();
bindService(intent, myConn,BIND_AUTO_CREATE);
}
class MyConn implements ServiceConnection{
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d(TAG, "onServiceConnected: ");
iservices = (Iservices)service;
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
}
//開始
public void startMusic(View view) {
iservices.callStartMusic();
}
//暫停
public void stopMusic(View view) {
iservices.callStopMusic();
}
//重新播放
public void restartMusic(View view) {
iservices.callRestartMusic();
}
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(myConn);
}
}
- 在服務裏面初始化MediaPlay(Iservices接口就不貼出來了)
public class MusicServer extends Service {
private static final String TAG = "Hsia";
private MediaPlayer mediaPlayer;
@Nullable
@Override
public IBinder onBind(Intent intent) {
return new MyBind();
}
@Override
public void onCreate() {
super.onCreate();
//初始化MediaPlay,根據官網copy一份,改下path就行了
mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
try {
mediaPlayer.setDataSource(Environment.getExternalStorageDirectory().getPath()+"/Download/"+"zxxpg.mp3");
mediaPlayer.prepareAsync();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void onDestroy() {
super.onDestroy();
}
private void startMusicService() {
mediaPlayer.start();
mediaPlayer.setLooping(false);
//更新進度條
updateSeekBar();
}
private void updateSeekBar() {
//獲取音頻的總文件長度
final int duration = mediaPlayer.getDuration();
//添加一個定時器,不斷的更新SeekBar
Timer timer = new Timer();
TimerTask timerTask = new TimerTask() {
@Override
public void run() {
//獲取當前播放進度
int currentPosition = mediaPlayer.getCurrentPosition();
//把數據發送出去
Message message = new Message();
Bundle bundle = new Bundle();
bundle.putInt("duration",duration);
bundle.putInt("currentPosition",currentPosition);
message.setData(bundle);
MainActivity.handler.sendMessage(message);
}
};
timer.schedule(timerTask,10,1000);
}
private void stopMusicService() {
mediaPlayer.pause();
}
private void restartMusicService() {
mediaPlayer.start();
}
private void setMusicProgress(int progress){
mediaPlayer.seekTo(progress);
}
//定義一箇中間人對象,把想要暴露出去的方法暴露出去
class MyBind extends Binder implements Iservices {
@Override
public void callStartMusic() {
startMusicService();
}
@Override
public void callStopMusic() {
stopMusicService();
}
@Override
public void callRestartMusic() {
restartMusicService();
}
@Override
public void callsetMusicProgress(int progress) {
setMusicProgress(progress);
}
}
}
Android視頻播放
VideoView實現(封裝好的簡單幾行代碼)
VideoView mVideo = (VideoView) findViewById(R.id.vv);
mVideo.setVideoPath("http://10.0.2.2/vd.3gp");
//添加一個進度條
mVideo.setMediaController(new MediaController(this));
mVideo.start();
SurfaceView實現(VideoView是基於SurfaceView的封裝)
SurfaceView mSV = (SurfaceView) findViewById(R.id.sv);
final SurfaceHolder holder = mSV.getHolder();
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(100);
final MediaPlayer mediaPlayer = new MediaPlayer();
//設置播放數據源
mediaPlayer.setDataSource("http://10.0.2.2/vd.mp4");
//準備播放
mediaPlayer.prepareAsync();
mediaPlayer.setDisplay(holder);
mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) {
mediaPlayer.start();
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
});
Android萬能播放器
是通過使用vitamio第三方工程來實現的。官網
www.vitamio.org
- 1、導入vitamio庫工程到AndroidStudio中。
AndroidStudio導入第三方庫工程
- 2、vitamio基本用法和VideoView差不多。
Android拍照和攝像
打開照相機
//打開照相機
public void openC(View view) {
//創建意圖對象
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(new File(Environment.getExternalStorageDirectory().getPath()+"/Download", "paizhao.png"))); // set the image file name
//開啓意圖 獲取結果
startActivityForResult(intent, 0);
}
打開攝像機
//打開攝像機
public void openS(View view) {
//創建意圖對象
Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(new File(Environment.getExternalStorageDirectory().getPath()+"/Download", "luxiang.3gp"))); // set the image file name
//開啓意圖 獲取結果
startActivityForResult(intent, 0);
}
關於作者
- 個人網站:北京互聯科技
- Email:[email protected]
- 項目地址:https://github.com/swordman20/Hsia05AndroidMultimedia.git