Android原生PDF功能实现,掌握了这些Android高级工程师必备知识,
7.3、代码实现:
树形控件的数据对象TreeNodeData:
/**
- 树形控件数据类(会用于页面间传输,所以需实现Serializable 或 Parcelable)
- 作者:齐行超
- 日期:2019.08.07
*/
public class TreeNodeData implements Serializable {
//名称
private String name;
//页码
private int pageNum;
//是否已展开(用于控制树形节点图片显示,即箭头朝向图片)
private boolean isExpanded;
//展示级别(1级、2级…,用于控制树形节点缩进位置)
private int treeLevel;
//子集(用于加载子节点,也用于判断是否显示箭头图片,如集合不为空,则显示)
private List subset;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getPageNum() {
return pageNum;
}
public void setPageNum(int pageNum) {
this.pageNum = pageNum;
}
public boolean isExpanded() {
return isExpanded;
}
public void setExpanded(boolean expanded) {
isExpanded = expanded;
}
public int getTreeLevel() {
return treeLevel;
}
public void setTreeLevel(int treeLevel) {
this.treeLevel = treeLevel;
}
public List getSubset() {
return subset;
}
public void setSubset(List subset) {
this.subset = subset;
}
}
树形控件适配器 : TreeAdapter
/**
- 树形控件适配器
- 作者:齐行超
- 日期:2019.08.07
*/
public class TreeAdapter extends RecyclerView.Adapter<TreeAdapter.TreeNodeViewHolder> {
//上下文
private Context context;
//数据
public List data;
//展示数据(由层级结构改为平面结构)
public List displayData;
//treelevel间隔(dp)
private int maginLeft;
//委托对象
private TreeEvent delegate;
/**
- 构造函数
- @param context 上下文
- @param data 数据
*/
public TreeAdapter(Context context, List data) {
this.context = context;
this.data = data;
maginLeft = UIUtils.dip2px(context, 20);
displayData = new ArrayList<>();
//数据转为展示数据
dataToDiaplayData(data);
}
/**
- 数据转为展示数据
- @param data 数据
*/
private void dataToDiaplayData(List data) {
for (TreeNodeData nodeData : data) {
displayData.add(nodeData);
if (nodeData.isExpanded() && nodeData.getSubset() != null) {
dataToDiaplayData(nodeData.getSubset());
}
}
}
/**
- 数据集合转为可显示的集合
*/
private void reDataToDiaplayData() {
if (this.data == null || this.data.size() == 0) {
return;
}
if(displayData == null){
displayData = new ArrayList<>();
}else{
displayData.clear();
}
dataToDiaplayData(this.data);
notifyDataSetChanged();
}
@Override
public TreeNodeViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.tree_item, null);
return new TreeNodeViewHolder(view);
}
@Override
public void onBindViewHolder(TreeNodeViewHolder holder, int position) {
final TreeNodeData data = displayData.get(position);
//设置图片
if (data.getSubset() != null) {
holder.img.setVisibility(View.VISIBLE);
if (data.isExpanded()) {
holder.img.setImageResource(R.drawable.arrow_h);
} else {
holder.img.setImageResource(R.drawable.arrow_v);
}
} else {
holder.img.setVisibility(View.INVISIBLE);
}
//设置图片偏移位置
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) holder.img.getLayoutParams();
int ratio = data.getTreeLevel() <= 0? 0 : data.getTreeLevel()-1;
params.setMargins(maginLeft * ratio, 0, 0, 0);
holder.img.setLayoutParams(params);
//显示文本
holder.title.setText(data.getName());
holder.pageNum.setText(String.valueOf(data.getPageNum()));
//图片点击事件
holder.img.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//控制树节点展开、折叠
data.setExpanded(!data.isExpanded());
//刷新数据源
reDataToDiaplayData();
}
});
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//回调结果
if(delegate!=null){
delegate.onSelectTreeNode(data);
}
}
});
}
@Override
public int getItemCount() {
return displayData.size();
}
/**
- 定义RecyclerView的ViewHolder对象
*/
class TreeNodeViewHolder extends RecyclerView.ViewHolder {
ImageView img;
TextView title;
TextView pageNum;
public TreeNodeViewHolder(View view) {
super(view);
img = view.findViewById(R.id.iv_arrow);
title = view.findViewById(R.id.tv_title);
pageNum = view.findViewById(R.id.tv_pagenum);
}
}
/**
- 接口:Tree事件
/
public interface TreeEvent{
/* - 当选择了某tree节点
- @param data tree节点数据
*/
void onSelectTreeNode(TreeNodeData data);
}
/**
- 设置Tree的事件
- @param treeEvent Tree的事件对象
*/
public void setTreeEvent(TreeEvent treeEvent){
this.delegate = treeEvent;
}
}
PDF目录树页面:PDFCatelogueActivity
/**
- UI页面:PDF目录
- 1、用于显示Pdf目录信息
- 2、点击tree item,带回Pdf页码到前一个页面
- 作者:齐行超
- 日期:2019.08.07
*/
public class PDFCatelogueActivity extends AppCompatActivity implements TreeAdapter.TreeEvent {
RecyclerView recyclerView;
Button btn_back;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
UIUtils.initWindowStyle(getWindow(), getSupportActionBar());
setContentView(R.layout.activity_catelogue);
initView();//初始化控件
setEvent();//设置事件
loadData();//加载数据
}
/**
- 初始化控件
*/
private void initView() {
btn_back = findViewById(R.id.btn_back);
recyclerView = findViewById(R.id.rv_tree);
}
/**
- 设置事件
*/
private void setEvent() {
btn_back.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
PDFCatelogueActivity.this.finish();
}
});
}
/**
- 加载数据
*/
private void loadData() {
//从intent中获得传递的数据
Intent intent = getIntent();
List catelogues = (List) intent.getSerializableExtra(“catelogues”);
//使用RecyclerView加载数据
LinearLayoutManager llm = new LinearLayoutManager(this);
llm.setOrientation(LinearLayoutManager.VERTICAL);
recyclerView.setLayoutManager(llm);
TreeAdapter adapter = new TreeAdapter(this, catelogues);
adapter.setTreeEvent(this);
recyclerView.setAdapter(adapter);
}
/**
- 点击tree item,带回Pdf页码到前一个页面
- @param data tree节点数据
*/
@Override
public void onSelectTreeNode(TreeNodeData data) {
Intent intent = new Intent();
intent.putExtra(“pageNum”, data.getPageNum());
setResult(Activity.RESULT_OK, intent);
finish();
}
}
PDF目录树的布局文件:activity_catelogue.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.RecyclerView
android:id="@+id/rv_tree"
android:layout_width=“match_parent”
android:layout_height=“match_parent”
android:layout_below="@+id/rl_top" />
8、PDF预览缩略图
这个功能算是本Demo中最为复杂的一个了:
如何将PDF某页面的内容转成图片?(默认是无法从pdfview中获得页面图片的)
如何减少图片内存的占用?(用户可能快速滑动列表,实时读取、显示多张图片)
如何优化PDF预览缩略图列表的滑动体验?(图片的获取需要一定时间)
如何合理的及时释放内存占用?
8.1、PDF预览缩略图列表的效果图
8.2、功能分析
1、如何将PDF某页面的内容转成图片?
查看android-pdfview的源码,无法通过PDFView控件获得某页面的图片,所以只能分析pdfium sdk的API了,如下图:
pdfium的renderPageBitmap方法可以将页面渲染成图片,不过需要传递一系列参数,而且要小心OutOfMemoryError。
那么,我们需要在代码中获取或者创建PdfiumCore对象,调用该方法,传递PdfDocument等参数,当bitmap使用完后,应及时释放掉。
2、如何减少内存的占用?
内存主要包括:
1、pdfium sdk加载pdf文件产生的内存(我们无法优化)
2、android-pdfview产生的内存(如果有需要,可改其源码)
3、我们将pdf页面转为缩略图,而产生的内存(必须优化,否则,容易oom)
3.1、当PdfiumCore、PdfDocument不再使用时,应及时关闭;
3.2、当缩略图不再使用时,应及时释放;
3.3、可使用LruCache临时缓存缩略图,防止重复调用renderPageBitmap获取图片;
3.4、LruCache应合理管控,当预览页面关闭时,必须清空缓存,以释放内存;
3.5、创建图片时,应使用RGB_565,能节约内存开销(一个像素点,占2字节)
3.6、创建图片时,应尽可能小的指定图片的宽高,能看清就行(图片占用的内存 = 宽 * 高 * 一个像素点占的字节数)
3、如何优化PDF预览缩略图列表的滑动体验?
查看pdfium源码,调用renderPageBitmap方法之前,还必须确保对应的页面已被打开,即调用了openPage方法。然而,这两个方法都需要一定时间才能执行完成的。
那么,如果我们直接在主线程中让每个RecylerVew的item分别调用renderPageBitmap方法,滑动列表时,会感觉特别卡,所以该方法只能放在子线程中调用了。
那么问题又来了,那么多子线程应该如何管控?
1、考虑CPU的占用,应使用线程池控制子线程并发、阻塞;
2、考虑到用户滑动速度,有可能某线程正执行或者阻塞着呢,页面已经滑过去了,那么,即使该线程加载出来了图片,也无法显示到列表中。所以对于RecyclerView已不可见的Item项对应的线程,应及时取消,防止做无用功,也节省了内存和cpu开销。
8.3、功能实现
预览缩略图工具类:PreviewUtils
/**
- 预览缩略图工具类
- 1、pdf页面转为缩略图
- 2、图片缓存管理(仅保存到内存,可使用LruCache,注意空间大小控制)
- 3、多线程管理(线程并发、阻塞、Future任务取消)
- 作者:齐行超
- 日期:2019.08.08
*/
public class PreviewUtils {
//图片缓存管理
private ImageCache imageCache;
//单例
private static PreviewUtils instance;
//线程池
ExecutorService executorService;
//线程任务集合(可用于取消任务)
HashMap<String, Future> tasks;
/**
- 单例(仅主线程调用,无需做成线程安全的)
- @return PreviewUtils实例对象
*/
public static PreviewUtils getInstance() {
if (instance == null) {
instance = new PreviewUtils();
}
return instance;
}
/**
- 默认构造函数
*/
private PreviewUtils() {
//初始化图片缓存管理对象
imageCache = new ImageCache();
//创建并发线程池(建议最大并发数大于1屏grid item的数量)
executorService = Executors.newFixedThreadPool(20);
//创建线程任务集合,用于取消线程执行
tasks = new HashMap<>();
}
/**
- 从pdf文件中加载图片
- @param context 上下文
- @param imageView 图片控件
- @param pdfiumCore pdf核心对象
- @param pdfDocument pdf文档对象
- @param pdfName pdf文件名称
- @param pageNum pdf页码
*/
public void loadBitmapFromPdf(final Context context,
final ImageView imageView,
final PdfiumCore pdfiumCore,
final PdfDocument pdfDocument,
final String pdfName,
final int pageNum) {
//判断参数合法性
if (imageView == null || pdfiumCore == null || pdfDocument == null || pageNum < 0) {
return;
}
try {
//缓存key
final String keyPage = pdfName + pageNum;
//为图片控件设置标记
imageView.setTag(keyPage);
Log.i(“PreViewUtils”, “加载pdf缩略图:” + keyPage);
//获得imageview的尺寸(注意:如果使用正常控件尺寸,太占内存了)
/int w = imageView.getMeasuredWidth();
int h = imageView.getMeasuredHeight();
final int reqWidth = w == 0 ? UIUtils.dip2px(context,100) : w;
final int reqHeight = h == 0 ? UIUtils.dip2px(context,150) : h;/
//内存大小= 图片宽度 * 图片高度 * 一个像素占的字节数(RGB_565 所占字节:2)
//注意:如果使用正常控件尺寸,太占内存了,所以此处指定四缩略图看着会模糊一点
final int reqWidth = 100;
final int reqHeight = 150;
//从缓存中取图片
Bitmap bitmap = imageCache.getBitmapFromLruCache(keyPage);
if (bitmap != null) {
imageView.setImageBitmap(bitmap);
return;
}
//使用线程池管理子线程
Future future = executorService.submit(new Runnable() {
@Override
public void run() {
//打开页面(调用renderPageBitmap方法之前,必须确保页面已open,重要)
pdfiumCore.openPage(pdfDocument, pageNum);
//调用native方法,将Pdf页面渲染成图片
final Bitmap bm = Bitmap.createBitmap(reqWidth, reqHeight, Bitmap.Config.RGB_565);
pdfiumCore.renderPageBitmap(pdfDocument, bm, pageNum, 0, 0, reqWidth, reqHeight);
//切回主线程,设置图片
if (bm != null) {
//将图片加入缓存
imageCache.addBitmapToLruCache(keyPage, bm);
//切回主线程加载图片
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
if (imageView.getTag().toString().equals(keyPage)) {
imageView.setImageBitmap(bm);
Log.i(“PreViewUtils”, “加载pdf缩略图:” + keyPage + “…已设置!!”);
}
}
});
}
}
});
//将任务添加到集合
tasks.put(keyPage, future);
} catch (Exception ex) {
ex.printStackTrace();
}
}
/**
- 取消从pdf文件中加载图片的任务
- @param keyPage 页码
*/
public void cancelLoadBitmapFromPdf(String keyPage) {
if (keyPage == null || !tasks.containsKey(keyPage)) {
return;
}
try {
Log.i(“PreViewUtils”, “取消加载pdf缩略图:” + keyPage);
Future future = tasks.get(keyPage);
if (future != null) {
future.cancel(true);
Log.i(“PreViewUtils”, “取消加载pdf缩略图:” + keyPage + “…已取消!!”);
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
/**
- 获得图片缓存对象
- @return 图片缓存
*/
public ImageCache getImageCache(){
return imageCache;
}
/**
- 图片缓存管理
*/
public class ImageCache {
//图片缓存
private LruCache<String, Bitmap> lruCache;
//构造函数
public ImageCache() {
//初始化 lruCache
//int maxMemory = (int) Runtime.getRuntime().maxMemory();
//int cacheSize = maxMemory/8;
int cacheSize = 1024 * 1024 * 30;//暂时设定30M
lruCache = new LruCache<String, Bitmap>(cacheSize) {
@Override
protected int sizeOf(String key, Bitmap value) {
return value.getRowBytes() * value.getHeight();
}
};
}
/**
- 从缓存中取图片
- @param key 键
- @return 图片
*/
public synchronized Bitmap getBitmapFromLruCache(String key) {
if(lruCache!= null) {
return lruCache.get(key);
}
return null;
}
/**
- 向缓存中加图片
- @param key 键
- @param bitmap 图片
*/
public synchronized void addBitmapToLruCache(String key, Bitmap bitmap) {
if (getBitmapFromLruCache(key) == null) {
if (lruCache!= null && bitmap != null)
lruCache.put(key, bitmap);
}
}
/**
- 清空缓存
*/
public void clearCache(){
if(lruCache!= null){
lruCache.evictAll();
}
}
}
}
grid列表适配器: GridAdapter
/**
- grid列表适配器
- 作者:齐行超
- 日期:2019.08.08
*/
public class GridAdapter extends RecyclerView.Adapter<GridAdapter.GridViewHolder> {
Context context;
PdfiumCore pdfiumCore;
PdfDocument pdfDocument;
String pdfName;
int totalPageNum;
public GridAdapter(Context context, PdfiumCore pdfiumCore, PdfDocument pdfDocument, String pdfName, int totalPageNum) {
this.context = context;
this.pdfiumCore = pdfiumCore;
this.pdfDocument = pdfDocument;
this.pdfName = pdfName;
this.totalPageNum = totalPageNum;
}
@Override
public GridViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.grid_item, null);
return new GridViewHolder(view);
}
@Override
public void onBindViewHolder(GridViewHolder holder, int position) {
//设置PDF图片
final int pageNum = position;
PreviewUtils.getInstance().loadBitmapFromPdf(context, holder.iv_page, pdfiumCore, pdfDocument, pdfName, pageNum);
//设置PDF页码
holder.tv_pagenum.setText(String.valueOf(position));
//设置Grid事件
holder.iv_page.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(delegate!=null){
delegate.onGridItemClick(pageNum);
}
}
});
return;
}
@Override
public void onViewDetachedFromWindow(GridViewHolder holder) {
super.onViewDetachedFromWindow(holder);
try {
//item不可见时,取消任务
if(holder.iv_page!=null){
PreviewUtils.getInstance().cancelLoadBitmapFromPdf(holder.iv_page.getTag().toString());
}
//item不可见时,释放bitmap (注意:本Demo使用了LruCache缓存来管理图片,此处可注释掉)
/Drawable drawable = holder.iv_page.getDrawable();
if (drawable != null) {
Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();
if (bitmap != null && !bitmap.isRecycled()) {
bitmap.recycle();
bitmap = null;
Log.i(“PreViewUtils”,“销毁pdf缩略图:”+holder.iv_page.getTag().toString());
}
}/
}catch (Exception ex){
ex.printStackTrace();
}
}
@Override
public int getItemCount() {
return totalPageNum;
}
class GridViewHolder extends RecyclerView.ViewHolder {
ImageView iv_page;
TextView tv_pagenum;
public GridViewHolder(View itemView) {
super(itemView);
iv_page = itemView.findViewById(R.id.iv_page);
tv_pagenum = itemView.findViewById(R.id.tv_pagenum);
}
}
/**
- 接口:Grid事件
/
public interface GridEvent{
/* - 当选择了某Grid项
- @param position tree节点数据
*/
void onGridItemClick(int position);
}
/**
- 设置Grid事件
- @param event Grid事件对象
*/
public void setGridEvent(GridEvent event){
this.delegate = event;
}
//Grid事件委托
private GridEvent delegate;
}
PDF预览缩略图页面:PDFPreviewActivity
/**
- UI页面:PDF预览缩略图(注意:此页面,需多关注内存管控)
- 1、用于显示Pdf缩略图信息
- 2、点击缩略图,带回Pdf页码到前一个页面
- 作者:齐行超
- 日期:2019.08.07
*/
public class PDFPreviewActivity extends AppCompatActivity implements GridAdapter.GridEvent {
RecyclerView recyclerView;
Button btn_back;
PdfiumCore pdfiumCore;
PdfDocument pdfDocument;
String assetsFileName;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
UIUtils.initWindowStyle(getWindow(), getSupportActionBar());
setContentView(R.layout.activity_preview);
initView();//初始化控件
setEvent();
loadData();
}
/**
- 初始化控件
*/
private void initView() {
btn_back = findViewById(R.id.btn_back);
recyclerView = findViewById(R.id.rv_grid);
}
/**
- 设置事件
*/
private void setEvent() {
btn_back.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//回收内存
recycleMemory();
PDFPreviewActivity.this.finish();
}
});
}
/**
- 加载数据
*/
private void loadData() {
//加载pdf文件
loadPdfFile();
//获得pdf总页数
int totalCount = pdfiumCore.getPageCount(pdfDocument);
//绑定列表数据
GridAdapter adapter = new GridAdapter(this, pdfiumCore, pdfDocument, assetsFileName, totalCount);
adapter.setGridEvent(this);
recyclerView.setLayoutManager(new GridLayoutManager(this, 3));
recyclerView.setAdapter(adapter);
}
/**
- 加载pdf文件
*/
private void loadPdfFile() {
Intent intent = getIntent();
if (intent != null) {
assetsFileName = intent.getStringExtra(“AssetsPdf”);
if (assetsFileName != null) {
loadAssetsPdfFile(assetsFileName);
} else {
Uri uri = intent.getData();
if (uri != null) {
loadUriPdfFile(uri);
}
}
}
}
/**
- 加载assets中的pdf文件
*/
void loadAssetsPdfFile(String assetsFileName) {
try {
File f = FileUtils.fileFromAsset(this, assetsFileName);
ParcelFileDescriptor pfd = ParcelFileDescriptor.open(f, ParcelFileDescriptor.MODE_READ_ONLY);
pdfiumCore = new PdfiumCore(this);
pdfDocument = pdfiumCore.newDocument(pfd);
} catch (Exception ex) {
ex.printStackTrace();
}
}
/**
- 基于uri加载pdf文件
*/
void loadUriPdfFile(Uri uri) {
try {
ParcelFileDescriptor pfd = getContentResolver().openFileDescriptor(uri, “r”);
最后
说一千道一万,不如自己去行动。要想在移动互联网的下半场是自己占有一席之地,那就得从现在开始,从今天开始,马上严格要求自己,既重视业务实现能力,也重视基础和原理。基础夯实好了,高楼才能够平地而起,稳如泰山。
最后为了帮助大家深刻理解Android相关知识点的原理以及面试相关知识,这里放上相关的我搜集整理的24套腾讯、字节跳动、阿里、百度2020-2021面试真题解析,我把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包知识脉络 + 诸多细节。
还有 高级架构技术进阶脑图、Android开发面试专题资料 帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。
点击:
《Android架构视频+BAT面试专题PDF+学习笔记》
即可免费获取~
载pdf文件
*/
void loadUriPdfFile(Uri uri) {
try {
ParcelFileDescriptor pfd = getContentResolver().openFileDescriptor(uri, “r”);
最后
说一千道一万,不如自己去行动。要想在移动互联网的下半场是自己占有一席之地,那就得从现在开始,从今天开始,马上严格要求自己,既重视业务实现能力,也重视基础和原理。基础夯实好了,高楼才能够平地而起,稳如泰山。
最后为了帮助大家深刻理解Android相关知识点的原理以及面试相关知识,这里放上相关的我搜集整理的24套腾讯、字节跳动、阿里、百度2020-2021面试真题解析,我把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包知识脉络 + 诸多细节。
还有 高级架构技术进阶脑图、Android开发面试专题资料 帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。
[外链图片转存中…(img-a6qQubpl-1644993352764)]
[外链图片转存中…(img-FstaYpWX-1644993352764)]
[外链图片转存中…(img-DV9RiQYc-1644993352765)]
点击:
《Android架构视频+BAT面试专题PDF+学习笔记》
即可免费获取~
网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。
Android原生PDF功能实现,掌握了这些Android高级工程师必备知识,相关推荐
- Android原生PDF功能实现,Android开发面试书籍
/** 加载PDF文件 */ private void loadPdf() { Intent intent = getIntent(); if (intent != null) { assetsFil ...
- Android原生计步功能的实现,记录当日步数(仿微信运动),不需要后台service
Android原生计步功能的实现,记录当日步数(仿微信运动),不需要后台service 概述:通过调用Android4.4以上系统自带的计步传感器Sensor.TYPE_STEP_COUNTER,实现 ...
- android原生分享功能,Android原生分享到微博、微信等平台的实现方式
在这个版本功能涉及到Android分享方式的变更,需求要求: 分享只支持新浪微博.微信,其他方式均去掉. 为了更好的测试还是要看下Android分享实现分享的方式,然后才能更好地评估测试时间和设计测试 ...
- android原生砸蛋功能开发,Android
在一关一关的不断磨练和熟能生巧之后,玩家们需要做的就是不断的让每一关都能获得高分数和至少三星的游戏效果,不然以后获得金蛋的机会就很少.此外,砸金蛋也是有技巧和方法需要知道的,而不是盲目的将游戏从头体验 ...
- android切换账户功能,共享不是噱头 Android 4.2多用户功能详解
喜欢刷版本号的谷歌在今年已经把Android从4.0刷到了4.2,而大部分机器连果冻豆都还没吃上,单核的二儿子已经跪在了4.2门前.好在4.1到4.2只是改动不大的小幅升级,名称还是果冻豆,新加的特色 ...
- android开发 pdf阅读器 第三方可,android pdf 阅读器开发, pdf demo, pdf第三方控件
demo library 下载 Activity调用的方法 public class PDFActivity extends Activity implements OnPageChangeListe ...
- android提取pdf中文字,使用iTextG從Android上的pdf文件中提取文本
當我試圖從SD卡中讀取pdf文件並從中提取文本時,什麼也沒有發生. 沒有錯誤,沒有警告,通知,也沒有結果文件. 我將源文件和結果都存儲在設備的SD卡的根文件夾中. 你們能幫我解決這個問題嗎? 這裏是我 ...
- android原生vr相机,谷歌最新应用让Android手机秒变VR相机
网易科技讯 12月4日消息,据国外媒体报道,尽管三星Gear VR最近大热,但谷歌纸板VR依然是享受虚拟现实最简单.最便宜的办法.只需使用一片纸板.一个尼龙搭扣.两个镜头和一部Android智能手机, ...
- Android 适配Dark Theme(暗黑模式),Android高级工程师必备知识
Android 10 提供 Force Dark 功能.此功能可让开发者快速实现深色主题背景,只需要在 style.xml 中的应用主题中添加这一行代码android:forceDarkAllowed ...
- Android高级工程师必备知识!Android学习路线指南,复习指南
导语 事情是这样的,一个关注我公众号很久了的朋友,最近跟我说要去面试阿里P6,其实他的水平P7是够了的,他开发了6年,一直在学习新的技术,Flutter,NDK,这些都有涉及,年纪也不是很大26岁,之 ...
最新文章
- 孟晚舟升任华为轮值董事长,任正非曾表态:她无技术背景,不会成为接班人...
- Atitit.软件GUI按钮与仪表盘--db数据库区--导入mysql sql错误的解决之道
- Mxnet TensorRT
- Linux高级篇——IO系统编程
- rowStyle设置Bootstrap Table行样式
- error CS1617: Invalid option 'latest' for /langversion; must be ISO-1, ISO-2, Default or an...
- 利用IDA Pro修改51单片机bin文件
- 第6章 为用户编程:终端控制和信号
- canvas卡通兔子萝卜飞行动画
- 邯郸玄天文化风景区- 开辟永年华创
- 工程师也该学习机器学习了!
- g++的英文版使用说明和选项
- linux su命令在哪里,Linux su命令
- Word2007 无法给尾部空格加下划线
- Bitwig Studio 4.0.1 x64 Windows+Linux 音乐制作宿主软件
- C++中空类占一字节原因详解
- ECCV2020Workshop-PAN-270k参数量SISR网络 | Efficient Image Super-Resolution Using Pixel Attention
- ATFX:离岸人民币“破7”,开始还是结束?
- 快速实现自定义控件开关按钮
- 简单的职工信息管理系统(运用了存储过程,和datagridview,dataset的设置)