編輯:關於Android編程
PlayService如下:
package com.iwanghang.drmplayer; import android.app.Service; import android.content.Intent; import android.media.MediaPlayer; import android.media.MediaPlayer.OnCompletionListener; import android.media.MediaPlayer.OnErrorListener; import android.net.Uri; import android.os.Binder; import android.os.IBinder; import com.iwanghang.drmplayer.utils.MediaUtils; import com.iwanghang.drmplayer.vo.Mp3Info; import java.io.IOException; import java.util.ArrayList; import java.util.Random; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * 音樂播放的服務組件 * 實現功能: * 播放 * 暫停 * 下一首 * 上一首 * 獲取當前歌曲的播放進度 * * 需要在AndroidManifest.xml添加以下代碼: ** * * 實現功能(播放模式play_mode): * 順序播放 * 隨機播放 * 單曲循環 */ public class PlayService extends Service implements OnCompletionListener,OnErrorListener{ private MediaPlayer mPlayer; private int currentPosition;//當前正在播放的歌曲的位置 ArrayListmp3Infos; private MusicUpdatrListener musicUpdatrListener; //創建一個單實力的線程,用於更新音樂信息 private ExecutorService es = Executors.newSingleThreadExecutor(); //播放模式 public static final int ORDER_PLAY = 1;//順序播放 public static final int RANDOM_PLAY = 2;//隨機播放 public static final int SINGLE_PLAY = 3;//單曲循環 private int play_mode = ORDER_PLAY;//播放模式,默認為順序播放 /** * @param play_mode * ORDER_PLAY = 1;//順序播放 * RANDOM_PLAY = 2;//隨機播放 * SINGLE_PLAY = 3;//單曲循環 */ //set方法 public void setPlay_mode(int play_mode) { this.play_mode = play_mode; } //get方法 public int getPlay_mode() { return play_mode; } private boolean isPause = false;//歌曲播放中的暫停狀態 public boolean isPause(){ return isPause; } public PlayService() { } public int getCurrentPosition(){ return currentPosition; } private Random random = new Random();//創建隨機對象 //MediaPlayer.Completion 播放完成 實現播放下一首功能 //播放完成以後,判斷播放模式(曲目循環方式) //為了實現循環後,可以顯示音樂信息,需要在PlayAcivity的change裡添加對應代碼 @Override public void onCompletion(MediaPlayer mp) { switch (play_mode){ case ORDER_PLAY://順序播放 next();//下一首 break; case RANDOM_PLAY://隨機播放 //currentPosition = random.nextInt(mp3Infos.size());//隨機下標為mp3Infos.size() //play(currentPosition); play(random.nextInt(mp3Infos.size())); break; case SINGLE_PLAY://單曲循環 play(currentPosition); break; default: break; } } //MediaPlayer.Error 播放錯誤 處理實現播放下一首功能出現的錯誤 @Override public boolean onError(MediaPlayer mp, int what, int extra) { mp.reset();//重啟 return false; } //內部類PlayBinder實現Binder,得到當前PlayService對象 class PlayBinder extends Binder{ public PlayService getPlayService(){ System.out.println("PlayService #1 " + PlayService.this); return PlayService.this; } } @Override public IBinder onBind(Intent intent) { return new PlayBinder();//通過PlayBinder拿到PlayService,給Activity調用 } @Override public void onCreate() { super.onCreate(); mPlayer = new MediaPlayer(); mPlayer.setOnCompletionListener(this);//注冊播放完成事件 mPlayer.setOnErrorListener(this);//注冊播放錯誤事件 mp3Infos = MediaUtils.getMp3Infos(this);//獲取Mp3列表 es.execute(updateSteatusRunnable);//更新進度值 } @Override public void onDestroy() { super.onDestroy(); //回收線程 if (es!=null && !es.isShutdown()){//當進度值等於空,並且,進度值沒有關閉 es.shutdown(); es = null; } } //利用Runnable來實現多線程 /** * Runnable * Java中實現多線程有兩種途徑:繼承Thread類或者實現Runnable接口. * Runnable接口非常簡單,就定義了一個方法run(),繼承Runnable並實現這個 * 方法就可以實現多線程了,但是這個run()方法不能自己調用,必須由系統來調用,否則就和別的方法沒有什麼區別了. * 好處:數據共享 */ Runnable updateSteatusRunnable = new Runnable() {//更新狀態 @Override public void run() { //不斷更新進度值 while (true){ //音樂更新監聽不為空,並且,媒體播放不為空,並且媒體播放為播放狀態 if(musicUpdatrListener!=null && mPlayer!=null && mPlayer.isPlaying()){ musicUpdatrListener.onPublish(getCurrentProgress());//獲取當前的進度值 } try { Thread.sleep(500);//500毫秒更新一次 } catch (InterruptedException e) { e.printStackTrace(); } } } }; //播放 public void play(int position){ if (position>=0 && position =mp3Infos.size()-1){//如果超出最大值,(因為第一首是0),說明已經是最後一首 currentPosition = 0;//回到第一首 }else { currentPosition++;//下一首 } play(currentPosition); } //上一首 previous public void prev(){ if (currentPosition-1<0){//如果上一首小於0,說明已經是第一首 currentPosition = mp3Infos.size()-1;//回到最後一首 }else { currentPosition--;//上一首 } play(currentPosition); } //默認開始播放的方法 public void start(){ if (mPlayer!=null && !mPlayer.isPlaying()){//判斷當前歌曲不等於空,並且沒有在播放的狀態 mPlayer.start(); } } //獲取當前是否為播放狀態,提供給MyMusicListFragment的播放暫停按鈕點擊事件判斷狀態時調用 public boolean isPlaying(){ if (mPlayer!=null){ return mPlayer.isPlaying(); } return false; } //獲取當前的進度值 public int getCurrentProgress(){ if(mPlayer!=null && mPlayer.isPlaying()){//mPlayer不為空,並且,為播放狀態 return mPlayer.getCurrentPosition(); } return 0; } //getDuration 獲取文件的持續時間 public int getDuration(){ return mPlayer.getDuration(); } //seekTo 尋找指定的時間位置 public void seekTo(int msec){ mPlayer.seekTo(msec); } //更新狀態的接口(PlayService的內部接口),並在BaseActivity中實現 public interface MusicUpdatrListener{//音樂更新監聽器 public void onPublish(int progress);//發表進度事件(更新進度條) public void onChange(int position); //更新歌曲位置.按鈕的狀態等信息 //聲明MusicUpdatrListener後,添加set方法 } //set方法 public void setMusicUpdatrListener(MusicUpdatrListener musicUpdatrListener) { this.musicUpdatrListener = musicUpdatrListener; } }
PlayActivity如下:
package com.iwanghang.drmplayer; import android.graphics.Bitmap; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.view.View; import android.view.View.OnClickListener; import android.widget.ImageView; import android.widget.SeekBar; import android.widget.TextView; import android.widget.Toast; import com.iwanghang.drmplayer.utils.ImageUtils; import com.iwanghang.drmplayer.utils.MediaUtils; import com.iwanghang.drmplayer.vo.Mp3Info; import java.util.ArrayList; import static com.iwanghang.drmplayer.PlayService.ORDER_PLAY; import static com.iwanghang.drmplayer.PlayService.RANDOM_PLAY; import static com.iwanghang.drmplayer.PlayService.SINGLE_PLAY; /** *PlayActivity 點擊MyMusicListFragment(本地音樂)底部UI中的專輯封面圖片打開的Activity */ public class PlayActivity extends BaseActivity implements OnClickListener{ private TextView textView1_title;//歌名 private ImageView imageView1_ablum;//專輯封面圖片 private SeekBar seekBar1;//進度條 private TextView textView1_start_time,textView1_end_time;//開始時間,結束時間 private ImageView imageView1_play_mode;//菜單 private ImageView imageView3_previous,imageView2_play_pause,imageView1_next;//上一首,播放暫停,下一首 private ImageView imageView1_ablum_reflection;//專輯封面圖片倒影 private ArrayListmp3Infos; //private int position;//當前播放的位置 private boolean isPause = false;//當前播放的是否為暫停狀態 private static final int UPDATE_TIME = 0x1;//更新播放事件的標記 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_music_play); //初始化界面信息 textView1_title = (TextView) findViewById(R.id.textView1_title);//歌名 imageView1_ablum = (ImageView) findViewById(R.id.imageView1_ablum);//專輯封面圖片 seekBar1 = (SeekBar) findViewById(R.id.seekBar1);//進度條 textView1_start_time = (TextView) findViewById(R.id.textView1_start_time);//開始時間 textView1_end_time = (TextView) findViewById(R.id.textView1_end_time);//結束時間 imageView1_play_mode = (ImageView) findViewById(R.id.imageView1_play_mode);//菜單 imageView3_previous = (ImageView) findViewById(R.id.imageView3_previous);//上一首 imageView2_play_pause = (ImageView) findViewById(R.id.imageView2_play_pause);//播放暫停 imageView1_next = (ImageView) findViewById(R.id.imageView1_next);//下一首 imageView1_ablum_reflection = (ImageView) findViewById(R.id.imageView1_ablum_reflection);//專輯封面圖片倒影 //注冊按鈕點擊監聽事件 imageView1_play_mode.setOnClickListener(this); imageView2_play_pause.setOnClickListener(this); imageView3_previous.setOnClickListener(this); imageView1_next.setOnClickListener(this); mp3Infos = MediaUtils.getMp3Infos(this); //bindPlayService();//綁定服務,異步過程,綁定後需要取得相應的值,來更新界面 myHandler = new MyHandler(this); //以下直接調用change()是不行的,因為異步問題,會發生NullPointerException空指針異常 //從MyMusicListFragment的專輯封面圖片點擊時間傳過來的position(當前播放的位置) //position = getIntent().getIntExtra("position",0); //change(position); //通過在BaseActivity中綁定Service,添加如下代碼實現change() //musicUpdatrListener.onChange(playService.getCurrentProgeress()); //從MyMusicListFragment的專輯封面圖片點擊時間傳過來的isPause(當前播放的是否為暫停狀態) //isPause = getIntent().getBooleanExtra("isPause",false); } //把播放服務的綁定和解綁放在onResume,onPause裡,好處是,每次回到當前Activity就獲取一次播放狀態 @Override protected void onResume() { super.onResume(); bindPlayService(); } @Override protected void onPause() { super.onPause(); unbindPlayService(); } @Override protected void onDestroy() { super.onDestroy(); unbindPlayService();//解綁服務 } //Handler用於更新已經播放時間 private static MyHandler myHandler; static class MyHandler extends Handler{ private PlayActivity playActivity; public MyHandler(PlayActivity playActivity){ this.playActivity = playActivity; } @Override public void handleMessage(Message msg) { super.handleMessage(msg); if (playActivity!=null){ switch (msg.what){ case UPDATE_TIME://更新時間(已經播放時間) playActivity.textView1_start_time.setText(MediaUtils.formatTime(msg.arg1)); break; } } } } @Override public void publish(int progress) { //以下是是直接調用線程,但是不能這樣做,會報錯,線程異常 //textView1_start_time.setText(MediaUtils.formatTime(progress)); //所以,我們需要使用Handler Message msg = myHandler.obtainMessage(UPDATE_TIME);//用於更新已經播放時間 msg.arg1 = progress;//用於更新已經播放時間 myHandler.sendMessage(msg);//用於更新已經播放時間 seekBar1.setProgress(progress); } @Override public void change(int position) { //if (this.playService.isPlaying()) {//獲取是否為播放狀態 System.out.println("PlayActivity #100 position = " + position); Mp3Info mp3Info = mp3Infos.get(position); textView1_title.setText(mp3Info.getTitle());//設置歌名 //獲取專輯封面圖片 Bitmap albumBitmap = MediaUtils.getArtwork(this, mp3Info.getId(), mp3Info.getAlbumId(), true, false); //改變播放界面專輯封面圖片 imageView1_ablum.setImageBitmap(albumBitmap); textView1_end_time.setText(MediaUtils.formatTime(mp3Info.getDuration()));//設置結束時間 imageView2_play_pause.setImageResource(R.mipmap.app_music_pause);//設置暫停圖片 seekBar1.setProgress(0);//設置當前進度為0 seekBar1.setMax((int)mp3Info.getDuration());//設置進度條最大值為MP3總時間 if (playService.isPlaying()){ imageView2_play_pause.setImageResource(R.mipmap.app_music_pause); }else { imageView2_play_pause.setImageResource(R.mipmap.app_music_play); } if(imageView1_ablum != null) { imageView1_ablum_reflection.setImageBitmap(ImageUtils.createReflectionBitmapForSingle(albumBitmap));//顯示倒影 } switch (playService.getPlay_mode()){ case ORDER_PLAY://順序播放 imageView1_play_mode.setImageResource(R.mipmap.app_mode_order); //imageView2_play_pause.setTag(ORDER_PLAY); break; case PlayService.RANDOM_PLAY://隨機播放 imageView1_play_mode.setImageResource(R.mipmap.app_mode_random); //imageView2_play_pause.setTag(RANDOM_PLAY); break; case PlayService.SINGLE_PLAY://單曲循環 imageView1_play_mode.setImageResource(R.mipmap.app_mode_single); //imageView2_play_pause.setTag(SINGLE_PLAY); break; default: break; } } //點擊事件 @Override public void onClick(View v) { switch (v.getId()){ case R.id.imageView2_play_pause: {//播放暫停按鈕 if (playService.isPlaying()) {//如果是播放狀態 imageView2_play_pause.setImageResource(R.mipmap.app_music_play);//設置播放圖片 playService.pause(); } else { if (playService.isPause()) { imageView2_play_pause.setImageResource(R.mipmap.app_music_pause);//設置暫停圖片 playService.start();//播放事件 } else { //只有打開APP沒有點擊任何歌曲,同時,直接點擊暫停播放按鈕時.才會調用 playService.play(0); } } break; } case R.id.imageView1_next:{//下一首按鈕 playService.next();//下一首 break; } case R.id.imageView3_previous:{//上一首按鈕 playService.prev();//上一首 break; } case R.id.imageView1_play_mode:{//循環模式按鈕 //int mode = (int) imageView1_play_mode.getTag(); switch (playService.getPlay_mode()){ case ORDER_PLAY: imageView1_play_mode.setImageResource(R.mipmap.app_mode_random); //imageView1_play_mode.setTag(RANDOM_PLAY); playService.setPlay_mode(RANDOM_PLAY); Toast.makeText(getApplicationContext(),"隨機播放",Toast.LENGTH_SHORT).show(); break; case RANDOM_PLAY: imageView1_play_mode.setImageResource(R.mipmap.app_mode_single); //imageView1_play_mode.setTag(SINGLE_PLAY); playService.setPlay_mode(SINGLE_PLAY); Toast.makeText(getApplicationContext(),"單曲循環",Toast.LENGTH_SHORT).show(); break; case SINGLE_PLAY: imageView1_play_mode.setImageResource(R.mipmap.app_mode_order); //imageView1_play_mode.setTag(ORDER_PLAY); playService.setPlay_mode(ORDER_PLAY); Toast.makeText(getApplicationContext(),"順序播放",Toast.LENGTH_SHORT).show(); break; } break; } default: break; } } }
ImageUtils如下:
package com.iwanghang.drmplayer.utils; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Bitmap.CompressFormat; import android.graphics.Bitmap.Config; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.ColorMatrix; import android.graphics.ColorMatrixColorFilter; import android.graphics.LinearGradient; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.PorterDuff; import android.graphics.PorterDuff.Mode; import android.graphics.PorterDuffXfermode; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Shader; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; /** * 圖片工具類 * Created by iwanghang on 16/4/25. */ public class ImageUtils { /**圖片的八個位置**/ public static final int TOP = 0; //上 public static final int BOTTOM = 1; //下 public static final int LEFT = 2; //左 public static final int RIGHT = 3; //右 public static final int LEFT_TOP = 4; //左上 public static final int LEFT_BOTTOM = 5; //左下 public static final int RIGHT_TOP = 6; //右上 public static final int RIGHT_BOTTOM = 7; //右下 /** * 圖像的放大縮小方法 * @param src 源位圖對象 * @param scaleX 寬度比例系數 * @param scaleY 高度比例系數 * @return 返回位圖對象 */ public static Bitmap zoomBitmap(Bitmap src, float scaleX, float scaleY) { Matrix matrix = new Matrix(); matrix.setScale(scaleX, scaleY); Bitmap t_bitmap = Bitmap.createBitmap(src, 0, 0, src.getWidth(), src.getHeight(), matrix, true); return t_bitmap; } /** * 圖像放大縮小--根據寬度和高度 * @param src * @param width * @param height * @return */ public static Bitmap zoomBimtap(Bitmap src, int width, int height) { return Bitmap.createScaledBitmap(src, width, height, true); } /** * 將Drawable轉為Bitmap對象 * @param drawable * @return */ public static Bitmap drawableToBitmap(Drawable drawable) { return ((BitmapDrawable)drawable).getBitmap(); } /** * 將Bitmap轉換為Drawable對象 * @param bitmap * @return */ public static Drawable bitmapToDrawable(Bitmap bitmap) { Drawable drawable = new BitmapDrawable(bitmap); return drawable; } /** * Bitmap轉byte[] * @param bitmap * @return */ public static byte[] bitmapToByte(Bitmap bitmap) { ByteArrayOutputStream out = new ByteArrayOutputStream(); bitmap.compress(Bitmap.CompressFormat.PNG, 100, out); return out.toByteArray(); } /** * byte[]轉Bitmap * @param data * @return */ public static Bitmap byteToBitmap(byte[] data) { if(data.length != 0) { return BitmapFactory.decodeByteArray(data, 0, data.length); } return null; } /** * 繪制帶圓角的圖像 * @param src * @param radius * @return */ public static Bitmap createRoundedCornerBitmap(Bitmap src, int radius) { final int w = src.getWidth(); final int h = src.getHeight(); // 高清量32位圖 Bitmap bitmap = Bitmap.createBitmap(w, h, Config.ARGB_8888); Paint paint = new Paint(); Canvas canvas = new Canvas(bitmap); canvas.drawARGB(0, 0, 0, 0); paint.setColor(0xff424242); // 防止邊緣的鋸齒 paint.setFilterBitmap(true); Rect rect = new Rect(0, 0, w, h); RectF rectf = new RectF(rect); // 繪制帶圓角的矩形 canvas.drawRoundRect(rectf, radius, radius, paint); // 取兩層繪制交集,顯示上層 paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN)); // 繪制圖像 canvas.drawBitmap(src, rect, rect, paint); return bitmap; } /** * 創建選中帶提示圖片 * @param context * @param srcId * @param tipId * @return */ public static Drawable createSelectedTip(Context context, int srcId, int tipId) { Bitmap src = BitmapFactory.decodeResource(context.getResources(), srcId); Bitmap tip = BitmapFactory.decodeResource(context.getResources(), tipId); final int w = src.getWidth(); final int h = src.getHeight(); Bitmap bitmap = Bitmap.createBitmap(w, h, Config.ARGB_8888); Paint paint = new Paint(); Canvas canvas = new Canvas(bitmap); //繪制原圖 canvas.drawBitmap(src, 0, 0, paint); //繪制提示圖片 canvas.drawBitmap(tip, (w - tip.getWidth()), 0, paint); return bitmapToDrawable(bitmap); } /** * 帶倒影的圖像 * @param src * @return */ public static Bitmap createReflectionBitmap(Bitmap src) { // 兩個圖像間的空隙 final int spacing = 4; final int w = src.getWidth(); final int h = src.getHeight(); // 繪制高質量32位圖 Bitmap bitmap = Bitmap.createBitmap(w, h + h / 2 + spacing, Config.ARGB_8888); // 創建燕X軸的倒影圖像 Matrix m = new Matrix(); m.setScale(1, -1); Bitmap t_bitmap = Bitmap.createBitmap(src, 0, h / 2, w, h / 2, m, true); Canvas canvas = new Canvas(bitmap); Paint paint = new Paint(); // 繪制原圖像 canvas.drawBitmap(src, 0, 0, paint); // 繪制倒影圖像 canvas.drawBitmap(t_bitmap, 0, h + spacing, paint); // 線性渲染-沿Y軸高到低渲染 Shader shader = new LinearGradient(0, h + spacing, 0, h + spacing + h / 2, 0x70ffffff, 0x00ffffff, Shader.TileMode.MIRROR); paint.setShader(shader); // 取兩層繪制交集,顯示下層。 paint.setXfermode(new PorterDuffXfermode(Mode.DST_IN)); // 繪制渲染倒影的矩形 canvas.drawRect(0, h + spacing, w, h + h / 2 + spacing, paint); return bitmap; } /** * 獨立的倒影圖像 * @param src * @return */ public static Bitmap createReflectionBitmapForSingle(Bitmap src) { final int w = src.getWidth(); final int h = src.getHeight(); // 繪制高質量32位圖 Bitmap bitmap = Bitmap.createBitmap(w, h / 2, Config.ARGB_8888); // 創建沿X軸的倒影圖像 Matrix m = new Matrix(); m.setScale(1, -1); Bitmap t_bitmap = Bitmap.createBitmap(src, 0, h / 2, w, h / 2, m, true); Canvas canvas = new Canvas(bitmap); Paint paint = new Paint(); // 繪制倒影圖像 canvas.drawBitmap(t_bitmap, 0, 0, paint); // 線性渲染-沿Y軸高到低渲染 Shader shader = new LinearGradient(0, 0, 0, h / 2, 0x70ffffff, 0x00ffffff, Shader.TileMode.MIRROR); paint.setShader(shader); // 取兩層繪制交集。顯示下層。 paint.setXfermode(new PorterDuffXfermode(Mode.DST_IN)); // 繪制渲染倒影的矩形 canvas.drawRect(0, 0, w, h / 2, paint); return bitmap; } public static Bitmap createGreyBitmap(Bitmap src) { final int w = src.getWidth(); final int h = src.getHeight(); Bitmap bitmap = Bitmap.createBitmap(w, h, Config.ARGB_8888); Canvas canvas = new Canvas(bitmap); Paint paint = new Paint(); // 顏色變換的矩陣 ColorMatrix matrix = new ColorMatrix(); // saturation 飽和度值,最小可設為0,此時對應的是灰度圖;為1表示飽和度不變,設置大於1,就顯示過飽和 matrix.setSaturation(0); ColorMatrixColorFilter filter = new ColorMatrixColorFilter(matrix); paint.setColorFilter(filter); canvas.drawBitmap(src, 0, 0, paint); return bitmap; } /** * 保存圖片 * @param src * @param filepath * @param format:[Bitmap.CompressFormat.PNG,Bitmap.CompressFormat.JPEG] * @return */ public static boolean saveImage(Bitmap src, String filepath, CompressFormat format) { boolean rs = false; File file = new File(filepath); try { FileOutputStream out = new FileOutputStream(file); if(src.compress(format, 100, out)) { out.flush(); //寫入流 } out.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return rs; } /** * 添加水印效果 * @param src 源位圖 * @param watermark 水印 * @param direction 方向 * @param spacing 間距 * @return */ public static Bitmap createWatermark(Bitmap src, Bitmap watermark, int direction, int spacing) { final int w = src.getWidth(); final int h = src.getHeight(); Bitmap bitmap = Bitmap.createBitmap(w, h, Config.ARGB_8888); Canvas canvas = new Canvas(bitmap); canvas.drawBitmap(src, 0, 0, null); if(direction == LEFT_TOP) { canvas.drawBitmap(watermark, spacing, spacing, null); } else if(direction == LEFT_BOTTOM){ canvas.drawBitmap(watermark, spacing, h - watermark.getHeight() - spacing, null); } else if(direction == RIGHT_TOP) { canvas.drawBitmap(watermark, w - watermark.getWidth() - spacing, spacing, null); } else if(direction == RIGHT_BOTTOM) { canvas.drawBitmap(watermark, w - watermark.getWidth() - spacing, h - watermark.getHeight() - spacing, null); } return bitmap; } /** * 合成圖像 * @param direction * @param bitmaps * @return */ public static Bitmap composeBitmap(int direction, Bitmap... bitmaps) { if(bitmaps.length < 2) { return null; } Bitmap firstBitmap = bitmaps[0]; for (int i = 0; i < bitmaps.length; i++) { firstBitmap = composeBitmap(firstBitmap, bitmaps[i], direction); } return firstBitmap; } /** * 合成兩張圖像 * @param firstBitmap * @param secondBitmap * @param direction * @return */ private static Bitmap composeBitmap(Bitmap firstBitmap, Bitmap secondBitmap, int direction) { if(firstBitmap == null) { return null; } if(secondBitmap == null) { return firstBitmap; } final int fw = firstBitmap.getWidth(); final int fh = firstBitmap.getHeight(); final int sw = secondBitmap.getWidth(); final int sh = secondBitmap.getHeight(); Bitmap bitmap = null; Canvas canvas = null; if(direction == TOP) { bitmap = Bitmap.createBitmap(sw > fw ? sw : fw, fh + sh, Config.ARGB_8888); canvas = new Canvas(bitmap); canvas.drawBitmap(secondBitmap, 0, 0, null); canvas.drawBitmap(firstBitmap, 0, sh, null); } else if(direction == BOTTOM) { bitmap = Bitmap.createBitmap(fw > sw ? fw : sw, fh + sh, Config.ARGB_8888); canvas = new Canvas(bitmap); canvas.drawBitmap(firstBitmap, 0, 0, null); canvas.drawBitmap(secondBitmap, 0, fh, null); } else if(direction == LEFT) { bitmap = Bitmap.createBitmap(fw + sw, sh > fh ? sh : fh, Config.ARGB_8888); canvas = new Canvas(bitmap); canvas.drawBitmap(secondBitmap, 0, 0, null); canvas.drawBitmap(firstBitmap, sw, 0, null); } else if(direction == RIGHT) { bitmap = Bitmap.createBitmap(fw + sw, fh > sh ? fh : sh, Config.ARGB_8888); canvas = new Canvas(bitmap); canvas.drawBitmap(firstBitmap, 0, 0, null); canvas.drawBitmap(secondBitmap, fw, 0, null); } return bitmap; } }
自定義View控件, 算是常見的用法. 剛接觸安卓開發的時候, 很多人習慣在不同的XML布局文件裡面Copy一大段代碼, 達到相同View不斷被使用的效果,但如果以後要改
在以支持多種屏幕尺寸為目標設計應用時,您可以在不同的布局配置中重復使用您的fragment從而根據可用的屏幕空間優化用戶體驗。例如,在手機設備上,由於采用單窗格用戶界面,
由於Android並未提供滑動開關之類的組件,所以我們需要自己去實現一個自定義的視圖組件來實現滑動開關效果。 這裡有一個示例代碼,它包括三個類:開關組件視圖、狀態監聽接口
本文實例講述了Android編程實現自定義PopupMenu樣式。分享給大家供大家參考,具體如下:PopupMenu是Android中一個十分輕量級的組件。與PopupW