Androidとデータバインディング:アクションの処理

少し前に、アプリケーションの異なる場所で同じアクションを処理する必要があるアプリケーションの開発を完了しました。 標準的な状況、そして、いつものように、開発者は怠け者で、クライアントであるように思えます-昨日は何でもしてください。 したがって、すべてを簡単に、美しく、そして最も重要なこととして、余分なコード行を少なくする必要があります。



プロジェクトの特徴は、ほとんどの画面に同じまたは非常に類似したエンティティが含まれており、ユーザーがそれらに対して多数の同一のアクションを実行できることでした。 この問題の解決方法については、この記事で説明します。



ソースデータ



Instagramが何であるかを知っているなら、「クローズド、コーポレート」という言葉を追加することで、プロジェクトの本質を明確に示すことができます。 要するに、これは写真またはビデオコンテンツを含む短いメモを公開し、コメントを表示し、「いいね」を付け、著者をお気に入りリストに追加し、出版物を個別に追跡し、友人を検索し、出版物を検索するなどの機能を持つ閉じたソーシャルネットワークです。 各出版物は異なる地域に属し、1つ以上のカテゴリに属する​​場合があります。



合計で、複数の画面(「画面」)があります。特定のカテゴリの出版物のリスト、出版物の詳細、コメントのリスト、追跡ユーザーのリスト(フォロー)、彼らの出版物、既にあなたをフォローしているユーザー(フォロワー)のリスト、評価の束を持つユーザープロファイルフォーム、カウンター、アバターなど



ほとんどすべての画面にユーザーアバターがあります(実際のユーザーリスト、アバターと著者名を含む出版物、または特定のユーザーからのコメント)。 また、さまざまなスクリーンショットには、フォロー/アンフォロー、いいね/嫌いボタン、カテゴリタグなどがあります。



アバターをクリックして、ユーザープロフィールを開く必要があります。 記事をクリックして-詳細を開きます。 「いいね」または「フォロー」アイコンをクリックすると、わかります...



応用アプローチ



通常の方法で問題にアプローチすると、解決策はそれほど難しくありません。 たとえば、クリックしてユーザープロファイルを開きます。



findViewById(R.id.some_id).setOnClickListener((v) -> openUserProfile()); void openUserProfile(){ Intent = new Intent(this, ProfileActivity.class); intent.putExtra(USER_ID, userId); startActivity(); }
      
      





「いいね」または「フォロー」の場合、それはすでにより複雑です。サーバーに要求を行い、その応答を待機し、結果に応じて、特定のリストアイテムのボタンの表示を変更する必要があります。 原則として、それほど複雑ではありません。 しかし、「いいね」と「フォロー」の両方がアプリケーションの多くの場所にある可能性があるため、再利用を容易にするために、最終的に行われた個々のクラスに処理を委任することは論理的です。 このようなアクションハンドラは、「アクション」(FollowUserAction、LikeAction、OpenProfileActionなど)と呼ばれていました。 特定の画面で処理されたすべてのアクションは収集され、特定のActionHandlerマネージャーを介して実行されます。 その結果、同じユーザープロファイル画面を開くと次のようになります。



  mActionHandler = new ActionHandler.Builder() .addAction(ActionType.PROFILE, new OpenProfileAction()) .build(); findViewById(R.id.some_id).setOnClickListener((v) -> openUserProfile()); ... void openUserProfile(){ mActionHandler.fireAction(ActionType.PROFILE, user); }
      
      





では、先に進みましょう。 不要なコードの量をさらに減らすために、Androidデータバインディングサポートを接続し、ビジネスロジックコードにActionHandlerのみを残します。 実行するアクション。レイアウトファイル自体に書き込むボタンをクリックして実行します。 たとえば、出版物のリストを含む画面の場合、次のものがあります。



  mBinding = DataBindingUtil.inflate(..., R.layout.item_post); mBinding.setActionHandler(getActionHandler()); mBinding.setPost(getPost()); void initActionHandler() { mActionHandler = new ActionHandler.Builder() .addAction(ActionType.PROFILE, new OpenProfileAction()) .addAction(ActionType.COMMENTS, new OpenCommentsAction()) .addAction(ActionType.POST_DETAILS, new OpenPostDetailsAction()) .addAction(ActionType.FOLLOW, new FollowUserAction()) .addAction(ActionType.LIKE, new LikeAction()) .addAction(ActionType.MENU, new CompositeAction((TitleProvider)(post) -> post.getTitle(), new ActionItem(ActionType.SOME_ACTION_1, new SomeMenuAction(), "Title 1"), new ActionItem(ActionType.SOME_ACTION_2, new SomeMenuAction(), "Title 2"), new ActionItem(ActionType.SOME_ACTION_3, new SomeMenuAction(), "Title 3"), .build(); }
      
      







item_post.xml

 <layout> <data> <variable name="actionHandler" type="com.example.handler.ActionHandler" /> <variable name="post" type="com.example.model.Post" /> </data> <FrameLayout> <ImageView android:id="@+id/avatar" ... app:actionHandler="@{actionHandler}" app:actionType="@{ActionType.PROFILE}" app:model="@{post}" /> <FrameLayout android:id="@+id/post_container" ... app:actionHandler="@{actionHandler}" app:actionType="@{ActionType.POST_DETAILS}" app:actionTypeLongClick="@{ActionType.MENU}" app:model="@{post}"> ... </FrameLayout> <TextView android:id="@+id/comments" ... app:actionHandler="@{actionHandler}" app:actionType="@{ActionType.COMMENTS}" app:model="@{post}" /> <ImageView android:id="@+id/like" ... app:actionHandler="@{actionHandler}" app:actionType="@{ActionType.LIKE}" app:model="@{post}" /> ... </FrameLayout> </layout>
      
      





ここで、たとえば、一部の画面でクリックしてプロファイルの開始をブロックするか、長押しで表示されるメニュー項目を追加/削除する必要がある場合( actionTypeLongClick = "@ {ActionType.MENU} )、追加または削除するだけです。 1つの場所に対応するアクション。



また、データバインディングを使用すると、アクション自体からモデルを変更(たとえば、「いいね」を追加して、RecyclerViewのnotifyDataSetChanged()を呼び出す追加のコールバックなしで画面上の変更をすぐに確認できます。



アクションの例を次に示します。



  public class OpenProfileAction extends IntentAction<IUserHolder> { @Override public boolean isModelAccepted(Object model) { return model instanceof IUserHolder; } @Nullable @Override public Intent getIntent(@Nullable View view, Context context, String actionType, IUserHolder model) { return ProfileActivity.getIntent(context, model); } @Override protected ActivityOptionsCompat prepareTransition(Context context, View view, Intent intent) { // Prepare shared element transition Activity activity = getActivityFromContext(context); if (activity == null) return null; return ActivityOptionsCompat .makeSceneTransitionAnimation(activity, view, ProfileActivity.TRANSITION_NAME); } } public class LikeAction extends RequestAction<ModelResponse<Like>, Post> { @Override public boolean isModelAccepted(Object model) { return model instanceof Post; } @Override protected Observable<ModelResponse<Like>> getRequest(Context context, RestApiClient apiClient, Post model) { return apiClient.setLike(model.postId, !model.isLiked); } protected void onSuccess(Context context, View view, String actionType, Post oldModel, ModelResponse<Like> response) { oldModel.setLiked(response.getModel().isLiked); // automatically rebind icon for "like" button } }
      
      







まとめ



したがって、アプリケーション内のアクションを処理する非常に柔軟で、非常に理解しやすいロジックを編成することが判明しました。



その結果、アイデアが発展し、さらに2つのプロジェクトが通過し、最終的には小さなライブラリ、 アクションハンドラーに変わりました。



現在、このような一般的に発生するアクションの空白があります。





参照資料



簡単な例のあるライブラリ-https://github.com/drstranges/ActionHandler



All Articles