安卓系统 基于StackView的卡片形式的动态小部件(类似于Tinder)

UPD:小部件不再是错误的,一切都已修复

大家好

一个月前,我第一次申请个人和家庭财务会计时发表了一篇文章

从那时起,我将名称更改为“预算和购物车”,并相应地添加了一些功能,使您可以快速创建购物车并使用它。

购物车小部件



创建购物车的机制非常简单,合乎逻辑,因此很方便:
在目录树中

您在类别中标记了多个项目,然后单击“添加到购物车”按钮。 带有根类别的职位列表以小部件卡的形式显示。 该动作可以根据需要重复多次。

结果,我们对购物清单进行了逻辑分组。
它的优点是它立即出现在该组的所有成员中,即 妈妈开了卡片,爸爸开车去买了所有东西。
单击卡,然后输入金额的表格就会打开。 结果,不是为每个项目输入金额,而是为整个父类别输入金额。 即 想法是,我们将详细类别用于购买计划,而父类别会更粗略地考虑金额。


实作

1.服务等级
public class StackWidgetService extends RemoteViewsService { @Override public void onCreate() { super.onCreate(); } @Override public RemoteViewsFactory onGetViewFactory(Intent intent) { return new StackRemoteViewsFactory(this.getApplicationContext(), intent); } } 


2.卡栈的适配器类
 public class StackRemoteViewsFactory implements RemoteViewsService.RemoteViewsFactory { private List<MoneyTransaction> mWidgetItems = new ArrayList<MoneyTransaction>(); private Context mContext; @Inject FirestoreRepository repository; public StackRemoteViewsFactory(Context context, Intent intent) { mContext = context; Injector.getApplicationComponent().inject(this); } public void onCreate() { } public void onDestroy() { // repository.getLiveOrders().removeObserver(observer); // In onDestroy() you should tear down anything that was setup for your data source, // eg. cursors, connections, etc. mWidgetItems.clear(); } public int getCount() { return mWidgetItems.size(); } public RemoteViews getViewAt(int position) { // position will always range from 0 to getCount() - 1. // We construct a remote views item based on our widget item xml file, and set the // text based on the position. RemoteViews rv = new RemoteViews(mContext.getPackageName(), R.layout.widget_stack_orders_item); if(position < mWidgetItems.size()) { MoneyTransaction moneyTransaction = mWidgetItems.get(position); String dateStr = FormatTool.getDateStringShort(moneyTransaction.getDate()); rv.setTextViewText(R.id.tv_date, dateStr); rv.setTextViewText(R.id.tv_correspondent, moneyTransaction.getCategoryObject().getName()); rv.setTextViewText(R.id.tv_comment, moneyTransaction.getComment()); Date today = FormatTool.getStartOfPeriod(Calendar.getInstance().getTime(), Constants.PERIODICITY_DAY); Date date = FormatTool.getStartOfPeriod(moneyTransaction.getDate(), Constants.PERIODICITY_DAY); long days = TimeUnit.MILLISECONDS.toDays(today.getTime() - date.getTime()); if(days < 0) { rv.setInt(R.id.widget_item, "setBackgroundResource", R.drawable.blue_with_border); } else if(days == 0) { rv.setInt(R.id.widget_item, "setBackgroundResource", R.drawable.green_with_border); } else if(days == 1) { rv.setInt(R.id.widget_item, "setBackgroundResource", R.drawable.orange_with_border); } else { rv.setInt(R.id.widget_item, "setBackgroundResource", R.drawable.red_with_border); } // Next, we set a fill-intent which will be used to fill-in the pending intent template // which is set on the collection view in StackWidgetProvider. Bundle extras = new Bundle(); extras.putString(FirestoreTables.PATH, moneyTransaction.getPath()); Intent fillInIntent = new Intent(); fillInIntent.setAction(OrdersWidgetProvider.CLICK_ACTION); fillInIntent.putExtras(extras); rv.setOnClickFillInIntent(R.id.widget_item, fillInIntent); } // Return the remote views object. return rv; } public RemoteViews getLoadingView() { // You can create a custom loading view (for instance when getViewAt() is slow.) If you // return null here, you will get the default loading view. return null; } public int getViewTypeCount() { return 1; } public long getItemId(int position) { return position; } public boolean hasStableIds() { return true; } public void onDataSetChanged() { mWidgetItems = repository.getOrdersSync(); } } 


3.小部件提供程序类
        .       -  2 : -             ( onDataSetChanged     ) -        -        . public class OrdersWidgetProvider extends AppWidgetProvider { public static final String CLICK_ACTION = "click_action"; private Observer observer; @Inject FirestoreRepository repository; public OrdersWidgetProvider() { super(); Injector.getApplicationComponent().inject(this); } @Override public void onReceive(Context context, Intent intent) { if(CLICK_ACTION.equals(intent.getAction())) { String path = intent.getStringExtra(FirestoreTables.PATH); repository.getTransaction(path) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(transaction -> { if(transaction != null) { Intent launchIntent = new Intent(context, TransactionDetailActivity.class); launchIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); launchIntent.putExtra(Constants.TRANSACTION, transaction); context.startActivity(launchIntent); } }); } super.onReceive(context, intent); } @Override public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { LiveData<List<MoneyTransaction>> liveData = repository.getLiveOrders(); if(observer != null && liveData.hasActiveObservers()) { liveData.removeObserver(observer); } observer = (Observer<List<MoneyTransaction>>) moneyTransactions -> { repository.setOrders(moneyTransactions); appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetIds, R.id.stack_view); }; liveData.observeForever(observer); // update each of the widgets with the remote adapter for (int i = 0; i < appWidgetIds.length; ++i) { // Here we setup the intent which points to the StackViewService which will // // provide the views for this collection. Intent intent = new Intent(context, StackWidgetService.class); intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetIds[i]); // // When intents are compared, the extras are ignored, so we need to embed the extras // // into the data so that the extras will not be ignored. intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME))); RemoteViews rv = new RemoteViews(context.getPackageName(), R.layout.widget_stack_orders_layout); rv.setRemoteAdapter(R.id.stack_view, intent); // // // The empty view is displayed when the collection has no items. It should be a sibling // // of the collection view. rv.setEmptyView(R.id.stack_view, R.id.empty_view); // // Here we setup the a pending intent template. Individuals items of a collection // // cannot setup their own pending intents, instead, the collection as a whole can // // setup a pending intent template, and the individual items can set a fillInIntent // // to create unique before on an item to item basis. Intent intent1 = new Intent(context, OrdersWidgetProvider.class); intent1.setAction(CLICK_ACTION); intent1.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetIds[i]); PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent1, PendingIntent.FLAG_UPDATE_CURRENT); rv.setPendingIntentTemplate(R.id.stack_view, pendingIntent); appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetIds[i], R.id.stack_view); appWidgetManager.updateAppWidget(appWidgetIds[i], rv); } super.onUpdate(context, appWidgetManager, appWidgetIds); } @Override public void onDisabled(Context context) { super.onDisabled(context); repository.getLiveOrders().removeObserver(observer); } } 


4.小部件布局
 <?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <StackView android:id="@+id/stack_view" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:loopViews="true"/> <LinearLayout android:id="@+id/empty_view" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@drawable/round_light_button_switcher" android:orientation="vertical"> <ImageView android:layout_width="wrap_content" android:layout_height="200dp" android:layout_margin="2dp" android:src="@drawable/beans_horizontal"/> <TextView style="@style/PrimaryDarkBold26" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:layout_marginLeft="20dp" android:layout_marginTop="-40dp" android:layout_marginRight="20dp" android:layout_marginBottom="20dp" android:gravity="bottom|center_horizontal" android:text="@string/empty_view_text" android:textStyle="bold"/> </LinearLayout> </FrameLayout> 


5.卡标记
 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/widget_item" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <TextView android:id="@+id/tv_correspondent" style="@style/White20" android:layout_width="match_parent" android:layout_height="50dp" android:ellipsize="end" /> <LinearLayout style="@style/WhiteDividerStyle" android:layout_marginTop="4dp"/> <TextView android:id="@+id/tv_comment" style="@style/White18" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="4dp" android:ellipsize="end" android:maxLines="20"/> <LinearLayout style="@style/WhiteDividerStyle"/> <TextView android:id="@+id/tv_date" style="@style/White18" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="4dp"/> </LinearLayout> 


6. XML提供程序描述
 <?xml version="1.0" encoding="utf-8"?> <appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android" android:initialLayout="@layout/widget_stack_orders_layout" android:minWidth="150dp" android:minHeight="200dp" android:previewImage="@drawable/widget"> </appwidget-provider> 

Source: https://habr.com/ru/post/zh-CN452998/


All Articles