35
35
import android .view .View ;
36
36
import android .view .ViewGroup ;
37
37
import android .view .Window ;
38
+ import android .widget .AdapterView ;
38
39
import android .widget .BaseAdapter ;
39
40
import android .widget .ImageView ;
40
41
import android .widget .TextView ;
47
48
48
49
import java .security .InvalidParameterException ;
49
50
import java .util .ArrayList ;
51
+ import java .util .Arrays ;
50
52
import java .util .List ;
51
53
import java .util .concurrent .atomic .AtomicBoolean ;
52
54
@@ -76,7 +78,10 @@ public class CompositeAction<M> extends BaseAction<M> implements OnActionFiredLi
76
78
77
79
// Flag for settings how a single action item should be fired.
78
80
// True for show a menu, false for fire action directly.
79
- private boolean mDisplayDialogForSingleAction = true ;
81
+ protected boolean mDisplayDialogForSingleAction = true ;
82
+
83
+ // True for show non accepted actions in menu as disabled, false to hide them.
84
+ protected boolean mShowNonAcceptedActions = false ;
80
85
81
86
/**
82
87
* Specific type of action which can contain a few other actions, show them as menu items,
@@ -89,7 +94,24 @@ public class CompositeAction<M> extends BaseAction<M> implements OnActionFiredLi
89
94
* if there is only single action in a menu.
90
95
*/
91
96
public CompositeAction (@ StringRes int titleResId , boolean displayDialogForSingleAction , ActionItem ... actions ) {
92
- this (new SimpleTitleProvider <M >(titleResId ), displayDialogForSingleAction , actions );
97
+ this (new SimpleTitleProvider <M >(titleResId ), displayDialogForSingleAction , false , actions );
98
+ }
99
+
100
+ /**
101
+ * Specific type of action which can contain a few other actions, show them as menu items,
102
+ * and fire an action, if corresponding item clicked.
103
+ *
104
+ * @param titleResId resource id for corresponding menu item's title
105
+ * @param actions action item, which contains menu item titles and actions,
106
+ * which will be fired if corresponding menu item selected
107
+ * @param displayDialogForSingleAction True for show a menu, false for fire action directly
108
+ * if there is only single action in a menu.
109
+ * @param showNonAcceptedActions true for show non accepted actions in menu as disabled,
110
+ * false to hide them
111
+ */
112
+ public CompositeAction (@ StringRes int titleResId , boolean displayDialogForSingleAction ,
113
+ boolean showNonAcceptedActions , ActionItem ... actions ) {
114
+ this (new SimpleTitleProvider <M >(titleResId ), displayDialogForSingleAction , showNonAcceptedActions , actions );
93
115
}
94
116
95
117
/**
@@ -101,7 +123,7 @@ public CompositeAction(@StringRes int titleResId, boolean displayDialogForSingle
101
123
* which will be fired if corresponding menu item selected
102
124
*/
103
125
public CompositeAction (@ StringRes int titleResId , ActionItem ... actions ) {
104
- this (new SimpleTitleProvider <M >(titleResId ), actions );
126
+ this (new SimpleTitleProvider <M >(titleResId ), true , false , actions );
105
127
}
106
128
107
129
/**
@@ -113,7 +135,7 @@ public CompositeAction(@StringRes int titleResId, ActionItem... actions) {
113
135
* which will be fired if corresponding menu item selected
114
136
*/
115
137
public CompositeAction (TitleProvider <M > titleProvider , ActionItem ... actions ) {
116
- this (titleProvider , true , actions );
138
+ this (titleProvider , true , false , actions );
117
139
}
118
140
119
141
/**
@@ -127,10 +149,28 @@ public CompositeAction(TitleProvider<M> titleProvider, ActionItem... actions) {
127
149
* if there is only single action in a menu.
128
150
*/
129
151
public CompositeAction (TitleProvider <M > titleProvider , boolean displayDialogForSingleAction , ActionItem ... actions ) {
152
+ this (titleProvider , displayDialogForSingleAction , false , actions );
153
+ }
154
+
155
+ /**
156
+ * Specific type of action which can contain a few other actions, show them as menu items,
157
+ * and fire an action, if corresponding item clicked.
158
+ *
159
+ * @param titleProvider provider for corresponding menu item's title
160
+ * @param actions action item, which contains menu item titles and actions,
161
+ * which will be fired if corresponding menu item selected
162
+ * @param displayDialogForSingleAction True for show a menu, false for fire action directly
163
+ * if there is only single action in a menu.
164
+ * @param showNonAcceptedActions true for show non accepted actions in menu as disabled,
165
+ * false to hide them
166
+ */
167
+ public CompositeAction (TitleProvider <M > titleProvider , boolean displayDialogForSingleAction ,
168
+ boolean showNonAcceptedActions , ActionItem ... actions ) {
130
169
if (actions == null ) throw new InvalidParameterException ("Provide at least one action" );
131
170
mActions = actions ;
132
171
mTitleProvider = titleProvider ;
133
172
mDisplayDialogForSingleAction = displayDialogForSingleAction ;
173
+ mShowNonAcceptedActions = showNonAcceptedActions ;
134
174
135
175
for (ActionItem item : mActions ) {
136
176
if (item .action instanceof BaseAction ) {
@@ -154,6 +194,22 @@ public void setShowAsPopupMenuEnabled(boolean showAsPopupMenuEnabled) {
154
194
mShowAsPopupMenuEnabled = showAsPopupMenuEnabled ;
155
195
}
156
196
197
+ /**
198
+ * Flag for settings how a single action item should be fired.
199
+ * @param displayDialogForSingleAction true for show a menu, false for fire action directly.
200
+ */
201
+ public void setDisplayDialogForSingleAction (boolean displayDialogForSingleAction ) {
202
+ mDisplayDialogForSingleAction = displayDialogForSingleAction ;
203
+ }
204
+
205
+ /**
206
+ * Flag for settings how non accepted action item should be showed in the menu.
207
+ * @param showNonAcceptedActions true for show non accepted actions in menu as disabled, false to hide them.
208
+ */
209
+ public void setShowNonAcceptedActions (boolean showNonAcceptedActions ) {
210
+ mShowNonAcceptedActions = showNonAcceptedActions ;
211
+ }
212
+
157
213
/**
158
214
* Check if there is at least one action which can handle given model
159
215
*
@@ -236,10 +292,23 @@ private void showMenu(final Context context, final View view, String actionType,
236
292
String title = mTitleProvider .getTitle (context , model );
237
293
AlertDialog .Builder builder = buildAlertDialog (context , view , actionType , model , title , menuItems );
238
294
final AlertDialog dialog = builder .create ();
295
+ if (mShowNonAcceptedActions ) {
296
+ final AdapterView .OnItemClickListener clickListener = dialog .getListView ().getOnItemClickListener ();
297
+ if (clickListener != null ) {
298
+ dialog .getListView ().setOnItemClickListener (new AdapterView .OnItemClickListener () {
299
+ @ Override
300
+ public void onItemClick (AdapterView <?> parent , View view , int position , long id ) {
301
+ if (menuItems .get (position ).action .isModelAccepted (model )) {
302
+ clickListener .onItemClick (parent , view , position , id );
303
+ }
304
+ }
305
+ });
306
+ }
307
+ }
239
308
if (title == null ) {
240
309
dialog .requestWindowFeature (Window .FEATURE_NO_TITLE );
241
310
}
242
- builder .show ();
311
+ dialog .show ();
243
312
}
244
313
}
245
314
@@ -251,6 +320,7 @@ private void showMenu(final Context context, final View view, String actionType,
251
320
*/
252
321
@ NonNull
253
322
protected List <ActionItem > prepareMenuListItems (M model ) {
323
+ if (mShowNonAcceptedActions ) return Arrays .asList (mActions );
254
324
int count = mActions .length ;
255
325
final List <ActionItem > menuItems = new ArrayList <>(count );
256
326
for (int index = 0 ; index < count ; index ++) {
@@ -281,14 +351,21 @@ protected PopupMenu buildPopupMenu(final Context context, final View view, final
281
351
final ActionItem item = menuItems .get (index );
282
352
//noinspection unchecked
283
353
menu .add (0 , index , 0 , item .titleProvider .getTitle (context , model ));
354
+ if (mShowNonAcceptedActions ) {
355
+ menu .getItem (index ).setEnabled (item .action .isModelAccepted (model ));
356
+ }
284
357
}
285
358
final AtomicBoolean activated = new AtomicBoolean (false );
286
359
popupMenu .setOnMenuItemClickListener (new PopupMenu .OnMenuItemClickListener () {
287
360
@ Override
288
361
public boolean onMenuItemClick (MenuItem item ) {
289
362
activated .set (true );
290
363
final ActionItem actionItem = menuItems .get (item .getItemId ());
291
- fireActionItem (context , view , actionItem .actionType , model , actionItem );
364
+ if (item .isEnabled ()) {
365
+ fireActionItem (context , view , actionItem .actionType , model , actionItem );
366
+ } else {
367
+ notifyOnActionDismiss ("The model is not accepted for selected action" , view , actionType , model );
368
+ }
292
369
return true ;
293
370
}
294
371
});
@@ -318,11 +395,15 @@ protected AlertDialog.Builder buildAlertDialog(final Context context, final View
318
395
final AlertDialog .Builder builder = new AlertDialog .Builder (context )
319
396
.setTitle (title );
320
397
321
- builder .setAdapter (new MenuItemsAdapter (getMenuItemLayoutResId (), menuItems , model ), new DialogInterface .OnClickListener () {
398
+ builder .setAdapter (new MenuItemsAdapter (getMenuItemLayoutResId (), menuItems , model , mShowNonAcceptedActions ), new DialogInterface .OnClickListener () {
322
399
@ Override
323
400
public void onClick (DialogInterface dialog , int which ) {
324
401
final ActionItem actionItem = menuItems .get (which );
325
- fireActionItem (context , view , actionItem .actionType , model , actionItem );
402
+ if (actionItem .action .isModelAccepted (model )) {
403
+ fireActionItem (context , view , actionItem .actionType , model , actionItem );
404
+ } else {
405
+ notifyOnActionDismiss ("Model is not acceptable for this action" , view , actionType , model );
406
+ }
326
407
}
327
408
}).setOnCancelListener (new DialogInterface .OnCancelListener () {
328
409
@ Override
@@ -538,12 +619,14 @@ private static class MenuItemsAdapter extends BaseAdapter {
538
619
private final int mItemLayoutResId ;
539
620
private final List <ActionItem > mItems ;
540
621
private final Object mModel ;
622
+ private final boolean mShowNonAcceptedActions ;
541
623
private final boolean mHasIcons ;
542
624
543
- public MenuItemsAdapter (@ LayoutRes int itemLayoutResId , List <ActionItem > menuItems , Object model ) {
625
+ public MenuItemsAdapter (@ LayoutRes int itemLayoutResId , List <ActionItem > menuItems , Object model , boolean showNonAcceptedActions ) {
544
626
mItemLayoutResId = itemLayoutResId ;
545
627
mItems = menuItems ;
546
628
mModel = model ;
629
+ mShowNonAcceptedActions = showNonAcceptedActions ;
547
630
mHasIcons = checkHasIcons (mItems );
548
631
}
549
632
@@ -557,20 +640,38 @@ private boolean checkHasIcons(List<ActionItem> items) {
557
640
@ Override
558
641
public View getView (int position , View convertView , ViewGroup parent ) {
559
642
Context context = parent .getContext ();
643
+ ViewHolder viewHolder ;
560
644
if (convertView == null ) {
561
645
convertView = LayoutInflater .from (context ).inflate (mItemLayoutResId , parent , false );
646
+ viewHolder = new ViewHolder (convertView );
647
+ convertView .setTag (R .id .viewHolder , viewHolder );
648
+ } else {
649
+ viewHolder = (ViewHolder ) convertView .getTag (R .id .viewHolder );
562
650
}
563
651
564
652
ActionItem item = mItems .get (position );
653
+
654
+ boolean modelAccepted = true ;
655
+ if (mShowNonAcceptedActions ) {
656
+ modelAccepted = item .action .isModelAccepted (mModel );
657
+ viewHolder .itemView .setEnabled (modelAccepted );
658
+ }
565
659
//noinspection unchecked
566
660
final String label = item .titleProvider .getTitle (context , mModel );
567
- (( TextView ) convertView . findViewById ( android . R . id . text1 )) .setText (label );
568
- ImageView imageView = ( ImageView ) convertView . findViewById ( android . R . id . icon );
661
+ viewHolder . textView .setText (label );
662
+
569
663
if (item .iconProvider != null ) {
570
664
//noinspection unchecked
571
665
Drawable icon = item .iconProvider .getIconDrawable (context , mModel );
572
- imageView .setVisibility (icon != null ? View .VISIBLE : View .GONE );
573
- imageView .setImageDrawable (icon );
666
+ if (icon != null ) {
667
+ imageView .setVisibility (View .VISIBLE );
668
+ imageView .setImageDrawable (icon );
669
+ if (mShowNonAcceptedActions ) {
670
+ viewHolder .imageView .setAlpha (modelAccepted ? 1.0f : 0.3f );
671
+ }
672
+ } else {
673
+ imageView .setVisibility (mHasIcons ? View .INVISIBLE : View .GONE );
674
+ }
574
675
} else {
575
676
imageView .setVisibility (mHasIcons ? View .INVISIBLE : View .GONE );
576
677
}
@@ -597,5 +698,17 @@ public boolean hasStableIds() {
597
698
public long getItemId (int position ) {
598
699
return position ;
599
700
}
701
+
702
+ private static class ViewHolder {
703
+ final View itemView ;
704
+ final TextView textView ;
705
+ final ImageView imageView ;
706
+
707
+ public ViewHolder (View itemView ) {
708
+ this .itemView = itemView ;
709
+ this .textView = (TextView ) itemView .findViewById (android .R .id .text1 );
710
+ this .imageView = (ImageView ) itemView .findViewById (android .R .id .icon );
711
+ }
712
+ }
600
713
}
601
714
}
0 commit comments