好多天没写博客了,就把这段时间遇到的问题写出来吧。
如何对Scrollview进行截屏,这种需求一般是作为分享截屏时需要的·,查了很多方法,开始是和我前面写的对webview截屏的方法做参考,后来发现完全不合要求,毕竟Scrollview是布局不是View控件。
废话不多说,见核心部分:主要是计算当前scrollview的总高度,超过手机屏幕的高度了。
使用for循环递归累加其内部的子控件的高度:
private ScrollView scrollView; scrollView = (ScrollView) findViewById(R.id.scrollview); int h = 0; for (int i = 0; i < scrollView.getChildCount(); i++) { h += scrollView.getChildAt(i).getHeight();} Bitmap bitmap = Bitmap.createBitmap(scrollView.getWidth(), h, Bitmap.Config.ARGB_8888); // Bitmap bitmap = scrollView.getDrawingCache(true); final Canvas c = new Canvas(bitmap); scrollView.draw(c); ByteArrayOutputStream stream = new ByteArrayOutputStream(); bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream); final byte[] picture = stream.toByteArray(); if (bitmap != null && !bitmap.isRecycled()) { bitmap = null;// 把原来的 bitmap.recycle().改成这个 }
代码如上即可给Scrollview进行截屏并转换为bitmap,和byte[]数组,你可以根据自己需要选择使用·········
这几天一直在看binder的结构,感叹这样天才的设计。
现在只研究到binder的native框架,在IPCThreadState以下,真正的driver和数据交换还需要进一步研究。在此记录一些目前的体会。
1.IInterface的作用
个人感觉,这个IInterface严格上讲,并不是Binder这个框架的一部分。
它的作用是提供了一个common的方式,可以将IBinder与Service进行显示的转换。
因为在进行IPC时,实际的service IXXXService要转换成IBinder,才能传递给ServiceManager进行注册检索,或者传递给Client进行调用。
而且Client拿到ServiceManager回传的IBinder以后,又要转换回IXXXService进行功能调用,所以IInterface产生了,提供了IXXXService与IBinder互相转换的功能。
(1)IXXXService转换IBinder实现:
IXXXService继承自IInterface,所以IInterface中的asBinder()方法,会将自身,也就是IXXXService转换成一个IBinder对象。
sp<IBinder> IInterface::asBinder()
{
return this ? onAsBinder() : NULL;
}
这个onAsBinder()是一个虚拟方法,实际上是有IInterface的两个子类BpInterface和BnInterface实现的。
BpInterface的实现:
inline IBinder* BpInterface<INTERFACE>::onAsBinder()
{
return remote(); //调用它的父类BpRefBase的remote()方法,返回IBinder,其实就是一个BpBinder
}
BnInterface的实现:
IBinder* BnInterface<INTERFACE>::onAsBinder()
{
return this; //就是返回自身,因为BnInterface就是从BBinder继承的,BBinder又是继承自IBinder
}
(2)IBinder转换IXXXService实现:
当一个Client拿到ServiceManager返回的IBinder时,需要转换为IXXXService接口,才能调用它的功能。
这个转换,是由IInterface中定义的宏DECLARE_META_INTERFACE来声明的asInterface完成的,并由宏IMPLEMENT_META_INTERFACE实现的。
将宏代码IMPLEMENT_META_INTERFACE展开后得到:
android::sp<IXXXService> I##INTERFACE::asInterface(
const android::sp<android::IBinder>& obj)
{
android::sp<IXXXService> intr;
if (obj != NULL) {
intr = static_cast<IXXXService*>(
obj->queryLocalInterface( //先调用queryLocalInterface,这个方法是IBinder定义的,默认实现是返回NULL,而在BBinder的子类BnInterface中,重载了该方法,返回this,而
//BpInterface并没有重载,使用IBinder的默认实现,返回NULL。
IXXXService::descriptor).get());
if (intr == NULL)
}
return intr;
}
总结一下,如果传进来的obj参数,是一个BBinder,就返回自身(这种情况应该是service和client在同一进程),如果是一个BpBinder,就new一个代理对象返回(这种情况应该是service和client在不同进程)。
2.IBinder, BBinder和BpBinder
这3个类,是对Android Binder框架的抽象,其实这个BBinder,改成BnBinder可能更形象一些。
但是要注意的是,一个IXXXService的继承图中,BpBinder并不在这个继承关系之中,也就是说BpBinder并没有子类。但是BBinder是在这个继承关系当中的,它的子类就是BnInterface。
换句话说,BBinder和BpBinder的功能并不是对称的,以前就是没有理解到这一点,才会一直很糊涂。
BpBinder的是存在于BpRefBase中的mRemote的成员变量中。从Client调用Service的过程中分析,就更清楚了。
假设有一个IXXXService接口:
class IXXXService : public IInterface {
....
public void helloWorld(const char* str);
....
}
(1)client调用service
client得到一个BpXXXService以后
(a)会调用BpXXXService实现的helloWorld,它会将str参数打包到Parcel中。然后调用remote()->transact(xxx)
(b)remote()是在BpXXXService的父类BpRefBase中实现的,返回的就是一个BpBinder.实际上调用的就是BpBinder的transact
(c)BpBinder的transact实现,就是直接调用IPCThreadState::self()->transact()发送数据。
(2)service接收client请求:
(a)通过IPCThreadState接收到client的请求后,首先会调用BBinder的transact方法。
(b)BBinder的transact方法又会调用子类实现的虚拟方法onTransact。这个虚拟方法是在BnXXXService中实现的。
(c)onTransact方法,会通过传递进来的参数来判断,需要调用IXXXService中的那个方法,示例中只有一个helloWorld方法。
(d)直接调用helloWorld,就会找到它的真正实现,也就是BnXXXService的子类XXXService中的helloWorld方法。
总结一下,从上面的流程当中就可以看出前文说的,BpBinder并不在继承关系当中,它只是一个打包数据,并通过IPCThreadState::self()->transact()方法发送出去。
而BBinder和BnXXXService的作用,就是接收IPCThreadState传递过来的信息,解包数据,并调用XXXService真正的实现。
IPC的数据处理,Binder Driver和ServiceManager学习后会继续分析总结。
今天意外的发现struct与class之间的一些区别。就是这些区别导致了在使用这两个类型的数据作为ListPicker的ItemSource时的效果不一样。
(1)使用struct类型对象作为ListPicker的ItemSource
首先定义了一个这样一个struct结构体:
public struct MonthListPickerItem { public long Month; public bool IsLunarMonth; public MonthListPickerItem(long month, bool isLunarMonth) { Month = month; IsLunarMonth = isLunarMonth; } };
然后这样使用它:
public partial class MonthListPicker : UserControl { #region Constants private int January = 1; private int December = 12; private int MonthsInYear = 12; #endregion #region Data Members private List<MonthListPickerItem> m_SelectedItems; #endregion #region Constructor public MonthListPicker() { InitializeComponent(); m_SelectedItems = new List<MonthListPickerItem>(); m_Era = Era.Solar; } #endregion #region Public Properties public List<MonthListPickerItem> SelectedItems { get { List<MonthListPickerItem> copy = new List<MonthListPickerItem>(); copy.AddRange(m_SelectedItems); return copy; } set { if (!m_SelectedItems.Equals(value)) { m_SelectedItems.Clear(); m_SelectedItems.AddRange(value); } } } public Era Era { get { return (Era)m_Era; } set { if (m_Era != value) { m_Era = value; NotifyEraPropertyChanged(value); } } } #endregion #region Event Handlers private void InitMonthLP() { List<MonthListPickerItem> monthsOfYear = new List<MonthListPickerItem>(); bool isLunarMonth = (Era == Era.Lunar); for (int i = January; i <= December; ++i) { monthsOfYear.Add(new MonthListPickerItem(i, isLunarMonth)); } MonthLP.ItemsSource = monthsOfYear; // 设置itemsource为12个月份 MonthLP.SelectedItems = m_SelectedItems; // 设置选中的月份 } private void NotifyEraPropertyChanged(Era era) { // do something } #endregion }这样的代码运行起来时没有问题的,ListPicker的SelectedItems会有选中效果。
(2)使用class类型对象作为ListPicker的ItemSource
但是当我将MonthListPickerItem改为class类型时,代码没有编译错误,但是在运行时设置SelectedItems没有效果,也就是没有item被选中。
例如:
public class MonthListPickerItem { public long Month{get;set;} public bool IsLunarMonth{get;set;} public MonthListPickerItem(long month, bool isLunarMonth) { Month = month; IsLunarMonth = isLunarMonth; } };
这到底是什么原因呢?
struct对象是在栈上创建的,而class对象则是在堆上创建的。在堆上创建的对象在数据块索引和地址空间方面会不一样,即使存储的内容一样;程序在设置选中项的时候,估计是这些方面做了判断,只要是class类型对象使用了new,实际上它们就永远不相等,因此也就是找不到要默认选中的选项。
因此会出现这样的选中项设置无效的情况吧。
------------------------------------------------------------------
但目前没有深究这些问题,实际情况是不是上面所说的这样也不能保证,先作为一个笔记吧,有时间再来确定一下。