android 4.4 shareactionprovider小结
来源:互联网 发布:apache主目录配置文件 编辑:程序博客网 时间:2024/06/07 04:50
前言:最近碰到一个问题,在gallery中播放一个图片,并要求共享的时候,发现遥控器点击ok之后,遥控器部分没用了,只有按了退出之后,才能重新使用。后来发现是因为share 的activity list是空的,所以,当前的focus是在空list上,back 之后才能重新回到share btn上。可是后来查看了一下code,发现跟猜测还是有的区别的。
如果关于actionbar的相关小结,在另一篇博文《android ActionBar小结》中记录了。
<item android:id="@+id/action_share" android:icon="@drawable/ic_menu_share_holo_light" android:title="@string/share" android:visible="false" android:actionProviderClass="android.widget.ShareActionProvider" android:showAsAction="never" />
menu中看到share是依赖ShareActionProvider。其实后来我发现,其中的icon可以不用设置。这里比较特殊,需要用到ShareActionProvider,而在ActivityChooserView中会对share相关的控件进行重新配置和布局。还例如,android:actionViewClass="android.widget.SearchView"属性也是不需要另外设置icon属性的,因为在SearchView中也同样做了布局和配置。
share的配置很简单:
item = menu.findItem(R.id.action_share); if (item != null) { mShareActionProvider = (ShareActionProvider) item.getActionProvider(); mShareActionProvider .setShareHistoryFileName("share_history.xml"); mShareActionProvider.setShareIntent(mShareIntent); }通过id获取到menu 的item,然后获取ShareActionProvider的实例,setShareIntent、setShareHistoryFileName即可。
其中setShareIntent的参数mShareIntent是从下面这个函数获取的:
public void setShareIntents(Intent sharePanoramaIntent, Intent shareIntent, ShareActionProvider.OnShareTargetSelectedListener onShareListener) { mSharePanoramaIntent = sharePanoramaIntent; if (mSharePanoramaActionProvider != null) { mSharePanoramaActionProvider.setShareIntent(sharePanoramaIntent); } mShareIntent = shareIntent; if (mShareActionProvider != null) { mShareActionProvider.setShareIntent(shareIntent); mShareActionProvider.setOnShareTargetSelectedListener( onShareListener); } }这个函数的调用是:
Intent shareIntent = createShareIntent(mCurrentPhoto); mActionBar.setShareIntents(panoramaIntent, shareIntent, PhotoPage.this);其中CreateShareIntent函数是:
private static Intent createShareIntent(MediaObject mediaObject) { int type = mediaObject.getMediaType(); return new Intent(Intent.ACTION_SEND) .setType(MenuExecutor.getMimeType(type)) .putExtra(Intent.EXTRA_STREAM, mediaObject.getContentUri()) .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); }
这就是share所需要的所有配置。
使用ShareActionProvider,必须提供一个intent,同时这个intent需要声明ACTION_SEND和表示数据的extras(如EXTRA_TEXT、 EXTRA_STREAM);
需要注意是,如果一个应用程序需要接受Share Intent发送的共享数据,那么需要在该应用程序的Manifest.xml文件中定义<intent-filter/>元素,指明应用组件想要接受的intent。例如,针对上面发送的共享数据,另一个接收数据应用的Manifest.xml文件应注册intent-filter如下:
<intent-filter android:label="@string/app_name"> <action android:name="android.intent.action.SEND" /> <data android:mimeType="*/*" /> <category android:name="android.intent.category.DEFAULT" /></intent-filter>
ShareActionProvider中code比较多,share的所有操作都在这里。不逐一解释。
public void setShareIntent(Intent shareIntent) { ActivityChooserModel dataModel = ActivityChooserModel.get(mContext, mShareHistoryFileName); dataModel.setIntent(shareIntent); }
会调用dataModel.setIntent()
public void setIntent(Intent intent) { synchronized (mInstanceLock) { if (mIntent == intent) { return; } mIntent = intent; mReloadActivities = true; ensureConsistentState(); } }
会调用ensureConsistentState()
private void ensureConsistentState() { boolean stateChanged = loadActivitiesIfNeeded(); stateChanged |= readHistoricalDataIfNeeded(); pruneExcessiveHistoricalRecordsIfNeeded(); if (stateChanged) { sortActivitiesIfNeeded(); notifyChanged(); } }这个函数调用的地方很多,但是不会重复调用,这里主要是获取所有activity的信息,和历史记录的数据。
private boolean loadActivitiesIfNeeded() { if (mReloadActivities && mIntent != null) { mReloadActivities = false; mActivities.clear(); List<ResolveInfo> resolveInfos = mContext.getPackageManager() .queryIntentActivities(mIntent, 0); final int resolveInfoCount = resolveInfos.size(); for (int i = 0; i < resolveInfoCount; i++) { ResolveInfo resolveInfo = resolveInfos.get(i); ActivityInfo activityInfo = resolveInfo.activityInfo; if (ActivityManager.checkComponentPermission(activityInfo.permission, android.os.Process.myUid(), activityInfo.applicationInfo.uid, activityInfo.exported) == PackageManager.PERMISSION_GRANTED) { mActivities.add(new ActivityResolveInfo(resolveInfo)); } } return true; } return false; }在第一次调用的时候mReloadActivities = false;这就保证了下次不会重复调用。
到这里所有的share信息就基本上确定了。所有的信息都会加入到list中。
private final List<ActivityResolveInfo> mActivities = new ArrayList<ActivityResolveInfo>();
显示的话,是通过一个view显示的:
@Override public View onCreateActionView() { // Create the view and set its data model. ActivityChooserModel dataModel = ActivityChooserModel.get(mContext, mShareHistoryFileName); ActivityChooserView activityChooserView = new ActivityChooserView(mContext); activityChooserView.setActivityChooserModel(dataModel); // Lookup and set the expand action icon. TypedValue outTypedValue = new TypedValue(); mContext.getTheme().resolveAttribute(R.attr.actionModeShareDrawable, outTypedValue, true); Drawable drawable = mContext.getResources().getDrawable(outTypedValue.resourceId); activityChooserView.setExpandActivityOverflowButtonDrawable(drawable); activityChooserView.setProvider(this); // Set content description. activityChooserView.setDefaultActionButtonContentDescription( R.string.shareactionprovider_share_with_application); activityChooserView.setExpandActivityOverflowButtonContentDescription( R.string.shareactionprovider_share_with); return activityChooserView; }
会新建一个ActivityChooserView,
public ActivityChooserView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); TypedArray attributesArray = context.obtainStyledAttributes(attrs, R.styleable.ActivityChooserView, defStyle, 0); mInitialActivityCount = attributesArray.getInt( R.styleable.ActivityChooserView_initialActivityCount, ActivityChooserViewAdapter.MAX_ACTIVITY_COUNT_DEFAULT); Drawable expandActivityOverflowButtonDrawable = attributesArray.getDrawable( R.styleable.ActivityChooserView_expandActivityOverflowButtonDrawable); attributesArray.recycle(); LayoutInflater inflater = LayoutInflater.from(mContext); inflater.inflate(R.layout.activity_chooser_view, this, true); mCallbacks = new Callbacks(); mActivityChooserContent = (LinearLayout) findViewById(R.id.activity_chooser_view_content); mActivityChooserContentBackground = mActivityChooserContent.getBackground(); mDefaultActivityButton = (FrameLayout) findViewById(R.id.default_activity_button); mDefaultActivityButton.setOnClickListener(mCallbacks); mDefaultActivityButton.setOnLongClickListener(mCallbacks); mDefaultActivityButtonImage = (ImageView) mDefaultActivityButton.findViewById(R.id.image); final FrameLayout expandButton = (FrameLayout) findViewById(R.id.expand_activities_button); expandButton.setOnClickListener(mCallbacks); expandButton.setAccessibilityDelegate(new AccessibilityDelegate() { @Override public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) { super.onInitializeAccessibilityNodeInfo(host, info); info.setCanOpenPopup(true); } }); expandButton.setOnTouchListener(new ForwardingListener(expandButton) { @Override public ListPopupWindow getPopup() { return getListPopupWindow(); } @Override protected boolean onForwardingStarted() { showPopup(); return true; } @Override protected boolean onForwardingStopped() { dismissPopup(); return true; } }); mExpandActivityOverflowButton = expandButton; mExpandActivityOverflowButtonImage = (ImageView) expandButton.findViewById(R.id.image); mExpandActivityOverflowButtonImage.setImageDrawable(expandActivityOverflowButtonDrawable); mAdapter = new ActivityChooserViewAdapter(); mAdapter.registerDataSetObserver(new DataSetObserver() { @Override public void onChanged() { super.onChanged(); updateAppearance(); } }); Resources resources = context.getResources(); mListPopupMaxWidth = Math.max(resources.getDisplayMetrics().widthPixels / 2, resources.getDimensionPixelSize(com.android.internal.R.dimen.config_prefDialogWidth)); }
所有的显示都在这个view中。
其中注意有两点:
1. expandButton.setOnTouchListener(new ForwardingListener(expandButton) {
其中ForwardingListener是:
public static abstract class ForwardingListener implements View.OnTouchListener, View.OnAttachStateChangeListener {是个在OnTouchListener的基础上,多了一些处理。
public ForwardingListener(View src) { mSrc = src; mScaledTouchSlop = ViewConfiguration.get(src.getContext()).getScaledTouchSlop(); mTapTimeout = ViewConfiguration.getTapTimeout(); src.addOnAttachStateChangeListener(this);}
/** * Returns the popup to which this listener is forwarding events. * <p> * Override this to return the correct popup. If the popup is displayed * asynchronously, you may also need to override * {@link #onForwardingStopped} to prevent premature cancelation of * forwarding. * * @return the popup to which this listener is forwarding events */public abstract ListPopupWindow getPopup();@Overridepublic boolean onTouch(View v, MotionEvent event) { final boolean wasForwarding = mForwarding; final boolean forwarding; if (wasForwarding) { forwarding = onTouchForwarded(event) || !onForwardingStopped(); } else { forwarding = onTouchObserved(event) && onForwardingStarted(); } mForwarding = forwarding; return forwarding || wasForwarding;}@Overridepublic void onViewAttachedToWindow(View v) {}@Overridepublic void onViewDetachedFromWindow(View v) { mForwarding = false; mActivePointerId = MotionEvent.INVALID_POINTER_ID; if (mDisallowIntercept != null) { mSrc.removeCallbacks(mDisallowIntercept); }}/** * Called when forwarding would like to start. * <p> * By default, this will show the popup returned by {@link #getPopup()}. * It may be overridden to perform another action, like clicking the * source view or preparing the popup before showing it. * * @return true to start forwarding, false otherwise */protected boolean onForwardingStarted() { final ListPopupWindow popup = getPopup(); if (popup != null && !popup.isShowing()) { popup.show(); } return true;}/** * Called when forwarding would like to stop. * <p> * By default, this will dismiss the popup returned by * {@link #getPopup()}. It may be overridden to perform some other * action. * * @return true to stop forwarding, false otherwise */protected boolean onForwardingStopped() { final ListPopupWindow popup = getPopup(); if (popup != null && popup.isShowing()) { popup.dismiss(); } return true;}/** * Observes motion events and determines when to start forwarding. * * @param srcEvent motion event in source view coordinates * @return true to start forwarding motion events, false otherwise */private boolean onTouchObserved(MotionEvent srcEvent) { final View src = mSrc; if (!src.isEnabled()) { return false; } final int actionMasked = srcEvent.getActionMasked(); switch (actionMasked) { case MotionEvent.ACTION_DOWN: mActivePointerId = srcEvent.getPointerId(0); if (mDisallowIntercept == null) { mDisallowIntercept = new DisallowIntercept(); } src.postDelayed(mDisallowIntercept, mTapTimeout); break; case MotionEvent.ACTION_MOVE: final int activePointerIndex = srcEvent.findPointerIndex(mActivePointerId); if (activePointerIndex >= 0) { final float x = srcEvent.getX(activePointerIndex); final float y = srcEvent.getY(activePointerIndex); if (!src.pointInView(x, y, mScaledTouchSlop)) { // The pointer has moved outside of the view. if (mDisallowIntercept != null) { src.removeCallbacks(mDisallowIntercept); } src.getParent().requestDisallowInterceptTouchEvent(true); return true; } } break; case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: if (mDisallowIntercept != null) { src.removeCallbacks(mDisallowIntercept); } break; } return false;}/** * Handled forwarded motion events and determines when to stop * forwarding. * * @param srcEvent motion event in source view coordinates * @return true to continue forwarding motion events, false to cancel */private boolean onTouchForwarded(MotionEvent srcEvent) { final View src = mSrc; final ListPopupWindow popup = getPopup(); if (popup == null || !popup.isShowing()) { return false; } final DropDownListView dst = popup.mDropDownList; if (dst == null || !dst.isShown()) { return false; } // Convert event to destination-local coordinates. final MotionEvent dstEvent = MotionEvent.obtainNoHistory(srcEvent); src.toGlobalMotionEvent(dstEvent); dst.toLocalMotionEvent(dstEvent); // Forward converted event to destination view, then recycle it. final boolean handled = dst.onForwardedEvent(dstEvent, mActivePointerId); dstEvent.recycle(); return handled;}在onTouch的时候,会调用到override的几个函数,getPopup()、onForwardingStarted()、onForwardingStopped()。
2. updateAppearance()
private void updateAppearance() { // Expand overflow button. if (mAdapter.getActivityCount() > 0) { mExpandActivityOverflowButton.setEnabled(true); } else { mExpandActivityOverflowButton.setEnabled(false); } // Default activity button. final int activityCount = mAdapter.getActivityCount(); final int historySize = mAdapter.getHistorySize(); if (activityCount==1 || activityCount > 1 && historySize > 0) { mDefaultActivityButton.setVisibility(VISIBLE); ResolveInfo activity = mAdapter.getDefaultActivity(); PackageManager packageManager = mContext.getPackageManager(); mDefaultActivityButtonImage.setImageDrawable(activity.loadIcon(packageManager)); if (mDefaultActionButtonContentDescription != 0) { CharSequence label = activity.loadLabel(packageManager); String contentDescription = mContext.getString( mDefaultActionButtonContentDescription, label); mDefaultActivityButton.setContentDescription(contentDescription); } } else { mDefaultActivityButton.setVisibility(View.GONE); } // Activity chooser content. if (mDefaultActivityButton.getVisibility() == VISIBLE) { mActivityChooserContent.setBackground(mActivityChooserContentBackground); } else { mActivityChooserContent.setBackground(null); } }其中多了一个mDefaultActivityButton,这样设计就是在share button的旁边多加一个button,用于记录上一次share的activity,会发现如果用于share 的list 中只有一个activity的时候,也会显示在mDefaultActivityButton位置上。
- android 4.4 shareactionprovider小结
- ShareActionProvider
- Android AppCompatActivity的ActionBar之SearchView、ShareActionProvider以及menu
- can not cast to android.support.v7.widget.ShareActionProvider
- ShareActionProvider使用
- ActionBar ShareActionProvider
- Android samples API Demos之UI篇1(ActionBarCompat-ShareActionProvider)
- ShareActionProvider的简单用法
- ShareActionProvider的简单用法
- ShareActionProvider的使用
- ShareActionProvider 分享API之 DynamicShareActionProvider
- 使用ShareActionProvider实现分享功能
- android 小结
- android小结
- Android小结
- android小结
- 在界面布局中使用ShareActionProvider
- ActionBarSherlock学习笔记——ShareActionProvider
- PAT_1007
- Java线程池使用说明
- xp 权限设置
- 建立嵌入式gdb调试环境
- ios 实现推送消息
- android 4.4 shareactionprovider小结
- WTL在Win8.1系统WM_DROPFILES无法响应的解决办法
- IO系统性能之一:衡量性能的几个指标
- POJ 1007
- redhat图形界面启动后出现桌面但是没有登录界面解决办法
- leetcode-Single Number II
- PAT_1008
- 插值算法
- miniui 选择器