Android ViewPager中顯示圖片與播放視頻的填坑記錄
ViewPager介紹
ViewPager的功能就是可以使視圖滑動(dòng),就像Lanucher左右滑動(dòng)那樣。
ViewPager用于實(shí)現(xiàn)多頁面的切換效果,該類存在于Google的兼容包android-support-v4.jar里面.
ViewPager:
1)ViewPager類直接繼承了ViewGroup類,所有它是一個(gè)容器類,可以在其中添加其他的view類。
2)ViewPager類需要一個(gè)PagerAdapter適配器類給它提供數(shù)據(jù)。
3)ViewPager經(jīng)常和Fragment一起使用,并且提供了專門的FragmentPagerAdapter和FragmentStatePagerAdapter類供Fragment中 的ViewPager使用。
4)在編寫ViewPager的應(yīng)用的使用,還需要使用兩個(gè)組件類分別是PagerTitleStrip類和PagerTabStrip類,PagerTitleStrip類直接繼承 自ViewGroup類,而PagerTabStrip類繼承PagerTitleStrip類,所以這兩個(gè)類也是容器類。但是有一點(diǎn)需要注意,在定義XML的layout 的時(shí)候,這兩個(gè)類必須是ViewPager標(biāo)簽的子標(biāo)簽,不然會(huì)出錯(cuò)。
本文將詳細(xì)介紹關(guān)于Android ViewPager中顯示圖片與播放視頻填坑的相關(guān)內(nèi)容,分享出來供大家參考學(xué)習(xí),下面話不多說了,來一起看看詳細(xì)的介紹吧。
一.需求來源與實(shí)現(xiàn)思路
1.最近項(xiàng)目需求中有用到需要在ViewPager中播放視頻和顯示圖片的功能,視頻是本地視頻,最開始的實(shí)現(xiàn)思路是ViewPager中根據(jù)當(dāng)前item位置對(duì)應(yīng)的是圖片還是視頻去初始化PhotoView和SurfaceView,同時(shí)銷毀時(shí)根據(jù)item的位置去判斷移除PhotoView和SurfaceView。
2.上面那種方式確實(shí)是可以實(shí)現(xiàn)的,但是存在2個(gè)問題,第一,MediaPlayer的生命周期不容易控制并且存在內(nèi)存泄漏問題。第二,連續(xù)三個(gè)item都是視頻時(shí),來回滑動(dòng)的過程中發(fā)現(xiàn)會(huì)出現(xiàn)上個(gè)視頻的最后一幀畫面的bug。
3.未提升用戶體驗(yàn),視頻播放器初始化完成前上面會(huì)覆蓋有該視頻的第一幀圖片,但是發(fā)現(xiàn)存在第一幀圖片與視頻第一幀信息不符的情況,后面會(huì)通過代碼給出解決方案。
4.圖片和視頻尺寸如何適配以保證不變形。
二.需要填的坑
1.對(duì)于MediaPlayer的生命周期不容易控制的本質(zhì)原因是這種實(shí)現(xiàn)思路上我的播放器只有1個(gè),頻繁的初始化和銷毀造成了問題,所以后面我更改了實(shí)現(xiàn)方式,一個(gè)item的視頻對(duì)應(yīng)一個(gè)播放器。
2.對(duì)于滑動(dòng)過程中發(fā)現(xiàn)會(huì)出現(xiàn)上個(gè)視頻的最后一幀畫面的bug,發(fā)現(xiàn)是surfaceView這個(gè)控件造成的,后面通過將播放的載體更換為TextureView完美解決該問題。
3.SurfaceView與TextureView的本質(zhì)異同
第一:兩者都能在獨(dú)立的線程中繪制和渲染,在專用的GPU線程中大大提高渲染的性能。
第二:SurfaceView專門提供了嵌入視圖層級(jí)的繪制界面,開發(fā)者可以控制該界面像Size等的形式,能保證界面在屏幕上的正確位置。但也有局限:
1.由于是獨(dú)立的一層View,更像是獨(dú)立的一個(gè)Window,不能加上動(dòng)畫、平移、縮放;
2.兩個(gè)SurfaceView不能相互覆蓋。
第三:Texture更像是一般的View,像TextView那樣能被縮放、平移,也能加上動(dòng)畫。TextureView只能在開啟了硬件加速的Window中使用,并且消費(fèi)的內(nèi)存要比SurfaceView多,并伴隨著1-3幀的延遲。
第四:屏幕鎖屏?xí)rSurfaceView會(huì)銷毀重建,TextureView不會(huì)!
三.具體實(shí)現(xiàn)核心代碼
1.ViewPager的初始化
mAdapter = ImageBrowseFragmentPagerAdapter(supportFragmentManager, this, imgs) imgs_viewpager.offscreenPageLimit = 1 imgs_viewpager.adapter = mAdapter imgs_viewpager.currentItem = mPosition //為了處理首次點(diǎn)擊時(shí)視頻播放的問題 val message = Message.obtain() message.what = START_PLAY_VIDEO mHandler.sendMessageDelayed(message, 200)
2.Handler處理消息
private val START_PLAY_VIDEO = 0 private var DELETE_VIDEO = 1 private var DELETE_VIDEO_START_PLAY = 2 private var mHandler = Handler(Handler.Callback { msg -> when (msg.what) { //開始播放視頻 START_PLAY_VIDEO -> NotifyDispatch.dispatch(PreviewPlayVideoEvent(mPosition)) //刪除視頻時(shí)刷新ui DELETE_VIDEO -> { mAdapter?.setImgs(imgs) } //解決刪除視頻時(shí)之后跳轉(zhuǎn)到另一個(gè)item,當(dāng)它是視頻時(shí)不繼續(xù)播放的問題 DELETE_VIDEO_START_PLAY -> NotifyDispatch.dispatch(PreviewPlayVideoEvent(mDeletePosition)) } true })
3.刪除視頻或圖片的處理邏輯
private fun deletePhotos(position: Int) { if (imgs!!.isEmpty()) { return } ThreadDispatch.right_now.execute({ var file: File? file = File(imgs.get(position)) if (file != null && file?.exists()!!) { val intent = Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE) val uri = Uri.fromFile(file) intent.data = uri sendBroadcast(intent) file?.delete() imgs.removeAt(position) } if (position == imgs.size) { mDeletePosition = position - 1 } else { mDeletePosition = position } val message = Message.obtain() message.what = DELETE_VIDEO mHandler.sendMessage(message) NotifyDispatch.dispatch(DeletePreviewPhotoEvent(imgs)) val message1 = Message.obtain() message1.what = DELETE_VIDEO_START_PLAY mHandler.sendMessageDelayed(message1, 200) if (imgs.isEmpty()) { finish() } }) // } }
4.ViewPager對(duì)應(yīng)的Adapter
package com.immomo.camerax.gui.view.adapter; import android.content.Context; import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentStatePagerAdapter; import android.support.v4.app.FragmentTransaction; import android.view.View; import android.view.ViewGroup; import com.immomo.camerax.gui.fragment.PreviewImgFragment; import com.immomo.camerax.gui.fragment.PreviewVideoFragment; import java.util.ArrayList; import java.util.List; /** * Created by liuxu on 2018/3/26. */ public class ImageBrowseFragmentPagerAdapter extends FragmentStatePagerAdapter { private Context mContext; private List<String> datas; private int mCurrentSelectedPosition = -1; private FragmentManager mFragmentManager; private FragmentTransaction mFragmentTransaction; private ArrayList<Fragment> mFragments = new ArrayList<>(); public ImageBrowseFragmentPagerAdapter(FragmentManager fm, Context context, List<String> datas) { super(fm); mFragmentManager = fm; mContext = context; this.datas = datas; } public void removeContext(){ mContext = null; } @Override public void setPrimaryItem(ViewGroup container, int position, Object object) { mCurrentSelectedPosition = position; } @Override public void startUpdate(ViewGroup container) { super.startUpdate(container); } public void setImgs(List<String> imgs) { this.datas = imgs; notifyDataSetChanged(); } //處理更新無效----刪除條目 @Override public int getItemPosition(Object object) { return POSITION_NONE; } public int getPrimaryItemPosition() { return mCurrentSelectedPosition; } public ImageBrowseFragmentPagerAdapter(FragmentManager fm) { super(fm); } @Override public boolean isViewFromObject(View view, Object object) { return view == ((Fragment) object).getView(); } @Override public Fragment getItem(int position) { Bundle bundle = new Bundle(); bundle.putString("url", datas.get(position)); bundle.putInt("position", position); if (datas.get(position).endsWith(".jpg")) { PreviewImgFragment previewImgFragment = new PreviewImgFragment(); previewImgFragment.setArguments(bundle); return previewImgFragment; } else { PreviewVideoFragment previewVideoFragment = new PreviewVideoFragment(); previewVideoFragment.setArguments(bundle); return previewVideoFragment; } } @Override public int getCount() { return datas == null ? 0 : datas.size(); } @Override public Object instantiateItem(ViewGroup container, int position) { return super.instantiateItem(container,position); } @Override public void destroyItem(ViewGroup container, int position, Object object) { super.destroyItem(container,position,object); } }
5顯示圖片對(duì)應(yīng)的Fragment
package com.immomo.camerax.gui.fragment; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import com.bumptech.glide.Glide; import com.immomo.camerax.R; import com.immomo.camerax.foundation.util.StatusBarUtils; import com.immomo.camerax.gui.view.ResizablePhotoView; /** * Created by liuxu on 2018/3/27. */ public class PreviewImgFragment extends Fragment { @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_preview_photo, null); ResizablePhotoView resizablePhotoView = view.findViewById(R.id.customPhotoView); String url = getArguments().getString("url"); Glide.with(getContext()).load(url).into(resizablePhotoView); resizablePhotoView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { getActivity().finish(); } }); return view; } @Override public void onPause() { super.onPause(); } @Override public void onResume() { super.onResume(); } @Override public void onDetach() { super.onDetach(); } @Override public void onDestroyView() { super.onDestroyView(); } @Override public void onStart() { super.onStart(); } @Override public void onDestroy() { super.onDestroy(); } @Override public void setUserVisibleHint(boolean isVisibleToUser) { super.setUserVisibleHint(isVisibleToUser); } }
6.圖片根據(jù)寬度適配高度的自定義View
package com.immomo.camerax.gui.view; import android.content.Context; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import com.github.chrisbanes.photoview.PhotoView; /** * Created by liuxu on 2018/4/7. */ public class ResizablePhotoView extends PhotoView { public ResizablePhotoView(Context context) { super(context); } public ResizablePhotoView(Context context, AttributeSet attr) { super(context, attr); } public ResizablePhotoView(Context context, AttributeSet attr, int defStyle) { super(context, attr, defStyle); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { Drawable d = getDrawable(); if (d != null){ int width = MeasureSpec.getSize(widthMeasureSpec); //高度根據(jù)使得圖片的寬度充滿屏幕計(jì)算而得 int height = (int) Math.ceil((float) width * (float) d.getIntrinsicHeight() / (float) d.getIntrinsicWidth()); setMeasuredDimension(width, height); }else { super.onMeasure(widthMeasureSpec, heightMeasureSpec); } } }
7.播放視頻對(duì)應(yīng)的Fragment
/** * Created by liuxu on 2018/3/27. */ public class PreviewVideoFragment extends Fragment { private ImageView mPhotoView; private TextureView mTextureView; private String mUrl; private int mPosition; private AndroidMediaPlayer mIjkVodMediaPlayer; private boolean mIsSelected; private boolean mIsFirstPrepared; private PreviewPlayVideoSubscriber mPreviewPlayVideoSubscriber = new PreviewPlayVideoSubscriber() { @Override public void onEventMainThread(PreviewPlayVideoEvent event) { super.onEventMainThread(event); MDLog.e("liuxu",event.getPosition()+""); if (event != null && event.getPosition() == mPosition) { //說明是當(dāng)前條目 if (mIjkVodMediaPlayer != null && !mIjkVodMediaPlayer.isPlaying()) { if (mTextureView != null) { mIjkVodMediaPlayer.setSurface(mSurface); mIjkVodMediaPlayer.prepareAsync(); mPhotoView.setVisibility(View.VISIBLE); } } mIsSelected = true; } else { if (mIjkVodMediaPlayer != null && mIjkVodMediaPlayer.isPlaying()) { mIjkVodMediaPlayer.pause(); mIjkVodMediaPlayer.stop(); } if (mPhotoView != null) { mPhotoView.setVisibility(View.VISIBLE); } mIsSelected = false; } } }; private String mWidth; private String mHeight; @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { mPreviewPlayVideoSubscriber.register(); View view = inflater.inflate(R.layout.fragment_preview_video, null); mPhotoView = view.findViewById(R.id.photoView); mTextureView = view.findViewById(R.id.surfaceView); mUrl = getArguments().getString("url"); mPosition = getArguments().getInt("position"); layoutPlayer(); loadVideoScreenshot(getContext(), mUrl, mPhotoView, 1); view.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { release(); getActivity().finish(); } }); initTextureMedia(); return view; } private void initTextureMedia() { mTextureView.setSurfaceTextureListener(mSurfaceTextureListener); } private void play(String url) { try { mIjkVodMediaPlayer = new AndroidMediaPlayer(); mIjkVodMediaPlayer.reset(); mIjkVodMediaPlayer.setDataSource(url); //讓MediaPlayer和TextureView進(jìn)行視頻畫面的結(jié)合 mIjkVodMediaPlayer.setSurface(mSurface); //設(shè)置監(jiān)聽 mIjkVodMediaPlayer.setOnBufferingUpdateListener((mp, percent) -> { }); mIjkVodMediaPlayer.setOnCompletionListener(mp -> { mp.seekTo(0); mp.start(); }); mIjkVodMediaPlayer.setOnInfoListener((mp1, what, extra) -> { if (what == IMediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START) { mPhotoView.setVisibility(View.GONE); mIsFirstPrepared = true; } return false; }); mIjkVodMediaPlayer.setOnErrorListener((mp, what, extra) -> false); mIjkVodMediaPlayer.setOnPreparedListener(mp -> { mp.start(); if (!mIsFirstPrepared){ }else { mPhotoView.setVisibility(View.GONE); } }); mIjkVodMediaPlayer.setScreenOnWhilePlaying(true);//在視頻播放的時(shí)候保持屏幕的高亮 if (mIsSelected){ //異步準(zhǔn)備 mIjkVodMediaPlayer.prepareAsync(); } } catch (Exception e) { e.printStackTrace(); } } private Surface mSurface; private TextureView.SurfaceTextureListener mSurfaceTextureListener = new TextureView.SurfaceTextureListener() { @Override public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { mSurface = new Surface(surface); play(mUrl); } @Override public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { } @Override public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { if (mSurface != null){ mSurface.release(); mSurface = null; } if (mTextureView != null){ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { mTextureView.releasePointerCapture(); } } release(); return false; } @Override public void onSurfaceTextureUpdated(SurfaceTexture surface) { } }; @Override public void onStart() { super.onStart(); } @Override public void onAttach(Context context) { super.onAttach(context); } @Override public void onDetach() { super.onDetach(); } @Override public void onPause() { MDLog.e("liuxu", "onPause" + mPosition); //處理鎖屏?xí)r播放器停止播放 if (mIjkVodMediaPlayer != null && mIjkVodMediaPlayer.isPlaying()){ mIjkVodMediaPlayer.pause(); mIjkVodMediaPlayer.stop(); } super.onPause(); } //屏幕打開時(shí)重新播放 @Override public void onResume() { MDLog.e("liuxu", "onResume" + mPosition); super.onResume(); if (mIsSelected && mIjkVodMediaPlayer != null && !mIjkVodMediaPlayer.isPlaying()) { mIjkVodMediaPlayer.prepareAsync(); } } @Override public void onDestroy() { MDLog.e("liuxu", "onDestroy"); release(); if (mPreviewPlayVideoSubscriber.isRegister()) { mPreviewPlayVideoSubscriber.unregister(); } super.onDestroy(); } private void release() { if (mIjkVodMediaPlayer == null) { return; } if (mIjkVodMediaPlayer.isPlaying()) { mIjkVodMediaPlayer.stop(); } mIjkVodMediaPlayer.release(); mIjkVodMediaPlayer = null; } @Override public boolean getUserVisibleHint() { return super.getUserVisibleHint(); } /** * 動(dòng)態(tài)設(shè)置視頻寬高信息 */ private void layoutPlayer() { //獲取視頻寬高比 getPlayInfo(mUrl); float ratio = Float.parseFloat(mHeight) / Float.parseFloat(mWidth); MDLog.e("type", mPosition + "ratio" + ratio); int type = 0; //添加容錯(cuò)值 if (ratio < MediaConstants.INSTANCE.getASPECT_RATIO_1_1().toFloat() + MediaConstants.INSTANCE.getSCREEN_DEFAULT_VALUE() && ratio > MediaConstants.INSTANCE.getASPECT_RATIO_1_1().toFloat() - MediaConstants.INSTANCE.getSCREEN_DEFAULT_VALUE()) { type = ScreenAdapterUtils.INSTANCE.getSCALE_TYPE_11(); } else if (ratio < MediaConstants.INSTANCE.getASPECT_RATIO_4_3().toFloat() + MediaConstants.INSTANCE.getSCREEN_DEFAULT_VALUE() && ratio > MediaConstants.INSTANCE.getASPECT_RATIO_4_3().toFloat() - MediaConstants.INSTANCE.getSCREEN_DEFAULT_VALUE()) { type = ScreenAdapterUtils.INSTANCE.getSCALE_TYPE_43(); MDLog.e("type", "43"); } else if (ratio < MediaConstants.INSTANCE.getASPECT_RATIO_16_9().toFloat() + MediaConstants.INSTANCE.getSCREEN_DEFAULT_VALUE() && ratio > MediaConstants.INSTANCE.getASPECT_RATIO_16_9().toFloat() - MediaConstants.INSTANCE.getSCREEN_DEFAULT_VALUE()) { type = ScreenAdapterUtils.INSTANCE.getSCALE_TYPE_169(); MDLog.e("type", "169"); } FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) mTextureView.getLayoutParams(); layoutParams.height = ScreenAdapterUtils.INSTANCE.getSurfaceHeight(type); mTextureView.setLayoutParams(layoutParams); FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) mPhotoView.getLayoutParams(); params.height = ScreenAdapterUtils.INSTANCE.getSurfaceHeight(type); mPhotoView.setLayoutParams(params); MDLog.e("params.height", params.height + ""); } private void getPlayInfo(String mUri) { android.media.MediaMetadataRetriever mmr = new android.media.MediaMetadataRetriever(); try { if (mUri != null) { mmr.setDataSource(mUri); } else { //mmr.setDataSource(mFD, mOffset, mLength); } //寬 mWidth = mmr.extractMetadata(android.media.MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH); //高 mHeight = mmr.extractMetadata(android.media.MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT); // mBitmap = mmr.getFrameAtTime(1 ); } catch (Exception ex) { } finally { mmr.release(); } } public static void loadVideoScreenshot(final Context context, String uri, ImageView imageView, long frameTimeMicros) { // 這里的時(shí)間是以微秒為單位 RequestOptions requestOptions = RequestOptions.frameOf(frameTimeMicros); requestOptions.set(FRAME_OPTION, MediaMetadataRetriever.OPTION_CLOSEST); requestOptions.transform(new BitmapTransformation() { @Override protected Bitmap transform(@NonNull BitmapPool pool, @NonNull Bitmap toTransform, int outWidth, int outHeight) { return toTransform; } @Override public void updateDiskCacheKey(MessageDigest messageDigest) { try { messageDigest.update((context.getPackageName() + "RotateTransform").getBytes("utf-8")); } catch (Exception e) { e.printStackTrace(); } } }); Glide.with(context).load(uri).apply(requestOptions).into(imageView); } }
4.結(jié)語
筆者使用這種方式實(shí)現(xiàn)了項(xiàng)目需求,但是由于本人接觸音視頻的相關(guān)內(nèi)容比較少,全是在不斷探索和學(xué)習(xí)中前進(jìn),如有不足之處請(qǐng)?jiān)u論指正,謝謝。大家共同學(xué)習(xí)共同進(jìn)步。
好了以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問大家可以留言交流,謝謝大家對(duì)腳本之家的支持。
- android使用surfaceview+MediaPlayer播放視頻
- Android中使用TextureView播放視頻
- Android編程實(shí)現(xiàn)播放視頻時(shí)切換全屏并隱藏狀態(tài)欄的方法
- Android仿搜狐視頻、微視等列表播放視頻功能
- Android編程實(shí)現(xiàn)播放視頻的方法示例
- Android多媒體教程之播放視頻的四種方法
- Android 播放視頻常見問題小結(jié)
- Android DragVideo實(shí)現(xiàn)播放視頻時(shí)任意拖拽的方法
- Android仿新浪微博/QQ空間滑動(dòng)自動(dòng)播放視頻功能
- android surfaceView實(shí)現(xiàn)播放視頻功能
相關(guān)文章
利用kotlin實(shí)現(xiàn)一個(gè)餅圖實(shí)例代碼
餅狀圖是以不同顏色的圓的切片表示的值。下面這篇文章主要給大家介紹了關(guān)于利用kotlin實(shí)現(xiàn)一個(gè)餅圖的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。2017-12-12Android WebView交互傳遞json字符串并解析的方法
這篇文章主要給大家介紹了關(guān)于Android中WebView交互傳遞json字符串并解析的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)各位Android開發(fā)者具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。2018-05-05Android使用文件進(jìn)行數(shù)據(jù)存儲(chǔ)的方法
這篇文章主要介紹了Android使用文件進(jìn)行數(shù)據(jù)存儲(chǔ)的方法,較為詳細(xì)的分析了Android基于文件實(shí)現(xiàn)數(shù)據(jù)存儲(chǔ)所涉及的相關(guān)概念與使用技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-09-09Android開發(fā)之設(shè)置開機(jī)自動(dòng)啟動(dòng)的幾種方法
這篇文章主要介紹了Android開發(fā)之設(shè)置開機(jī)自動(dòng)啟動(dòng)的幾種方法的相關(guān)資料,這里提供三種方法幫助大家實(shí)現(xiàn)這樣的功能,需要的朋友可以參考下2017-08-08Android 沉浸式改變小米魅族狀態(tài)欄顏色的實(shí)例代碼
這篇文章主要介紹了Android 沉浸式改變小米魅族狀態(tài)欄顏色的實(shí)例代碼,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2017-02-02Android的ImageButton當(dāng)顯示Drawable圖片時(shí)就不顯示文字
Android提供的ImageButton當(dāng)顯示Drawable圖片時(shí)就不會(huì)再顯示文字了,下面與大家分享下3種解決方法,不會(huì)的朋友可以了解下哈2013-06-06Android使用AsyncTask實(shí)現(xiàn)多線程下載的方法
這篇文章主要介紹了Android使用AsyncTask實(shí)現(xiàn)多線程下載的方法,以完整實(shí)例形式詳細(xì)分析了Android使用AsyncTask實(shí)現(xiàn)多線程下載的功能代碼,界面布局及權(quán)限控制的具體方法,需要的朋友可以參考下2016-03-03Android開發(fā)筆記之: 數(shù)據(jù)存儲(chǔ)方式詳解
本篇文章是對(duì)Android中數(shù)據(jù)存儲(chǔ)方式進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-05-05