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位置上。





0 0
原创粉丝点击