使用 Executor 将 Master/Detail Flow 应用程序连接到服务器(新线程)

Connecting a Master/Detail Flow app to a server using Executor (New Thread)

我正在尝试在 android studio 中使用 Master/Detail 流模板制作一个应用程序,该应用程序从 rss 提要 link 中读取文章并将它们显示为左侧的列表手边并在右侧显示主要内容。我需要使用 Executor 连接到服务器以创建一个新线程,并将 Master/Detail 流模板中的占位符内容替换为提要中的信息。我最困惑的是如何使用 Executor 实际正确连接到服务器以及如何在 class 上实现它,其中所有内容都被放入列表中。请让我知道我应该在 classes 和方法中更新什么。

这是我正在使用的提要:http://www.goal.com/en/feeds/news?fmt=rss

这是 class 项目将被放入左侧列表的位置:

/**
 * This class is to set up the content that will fill the recycler view. As of now it is   filled with Dummy content. Real
 * content will be implemented once server is connected
 */
public class SoccerAPI {



/**
 * Array of dummy content to be displayed in the recyclerview
 */
public static final List<articles> ITEMS = new ArrayList<articles>();

/**
 * Using Hashmap to search the array by id
 */
public static final Map<String, articles> ITEM_MAP = new HashMap<String, articles>();

private static final int COUNT = 25;

static {
    // Adds dummy content to the array
    for (int i = 1; i <= COUNT; i++) {
        addItem(createArticleItem(i));
    }
}

/**
 * adds items to both the array and Hashmap
 * @param item
 */
private static void addItem(articles item) {
    ITEMS.add(item);
    ITEM_MAP.put(item.id, item);
}

/**
 * Creates the item "articles" which is the information from the array
 * @param position
 * @return
 */
private static articles createArticleItem(int position) {
    return new articles(String.valueOf(position), "Item " + position, makeDetails(position));
}

/**
 * Takes the details in the array and displays it in the recycler view
 * @param position
 * @return
 */

private static String makeDetails(int position) {
    StringBuilder builder = new StringBuilder();
    builder.append("Details about Item: ").append(position);
    for (int i = 0; i < position; i++) {
        builder.append("\nMore details information here.");
    }
    return builder.toString();
}

/**
 * A getters and setters for the dummy content.
 */
public static class articles {
    public final String id;
    public final String content;
    public final String details;

    public articles(String id, String content, String details) {
        this.id = id;
        this.content = content;
        this.details = details;
    }

    /**
     * Returns a string version of the dummy content
     * @return
     */

    @Override
    public String toString() {
        return content;
    }
}

}

这里是主机activity:

public class SoccerItemDetailHostActivity extends AppCompatActivity {

/**
 * declaring the float variable rateValue to be used in the sharedPreferences for the ratingBar
 */
   private float rateValue;

    @Override
    protected void onStart() {
    super.onStart();
    AlertDialog.Builder mBuild = new AlertDialog.Builder(SoccerItemDetailHostActivity.this);
    mBuild.setTitle("Please rate the app");
    SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
    SharedPreferences.Editor editor = preferences.edit();
    Float ratinginsharedpreference = preferences.getFloat("Rating", 0);


    View mView = getLayoutInflater().inflate(R.layout.soccerratingbar, null);
    mBuild.setView(mView);
    AlertDialog dialog = mBuild.create();
    final RatingBar ratebar = (RatingBar) mView.findViewById(R.id.ratingBar);
    if (ratinginsharedpreference != 0) {
        ratebar.setRating(ratinginsharedpreference);
    }

    ratebar.setOnRatingBarChangeListener(new RatingBar.OnRatingBarChangeListener() {
        @Override
        public void onRatingChanged(RatingBar ratingBar, float rating, boolean fromUser) {
            rateValue = rating;
            Toast.makeText(SoccerItemDetailHostActivity.this, "" + rating, Toast.LENGTH_SHORT).show();


        }
    });

    Button btnSubmit = (Button) mView.findViewById(R.id.btnSubRating);
    btnSubmit.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {

            editor.putFloat("Rating", rateValue);
            editor.apply();

            Toast.makeText(getApplicationContext(), "Thank you for rating the app", Toast.LENGTH_LONG).show();
            dialog.cancel();
        }
    });

    dialog.show();



}

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    ActivitySocceritemDetailBinding binding = ActivitySocceritemDetailBinding.inflate(getLayoutInflater());
    setContentView(binding.getRoot());

    /**
     * Setting up the buttons to be displayed on screen. Finding the button value in the XML by the id
     */

    Button save = findViewById(R.id.save);
    ImageButton help = findViewById(R.id.help);

    /**
     * Setting up onClickListeners for each of the buttons. Right now only the messages are implemented
     * until the app is connected to the server
     */

    save.setOnClickListener(clk -> {

                Snackbar snackbar = Snackbar.make(binding.getRoot(), "You saved the articles to your favourites", Snackbar.LENGTH_LONG);
                snackbar.show();
            }
    );


    help.setOnClickListener(clk -> {
        AlertDialog alertDialog = new AlertDialog.Builder(SoccerItemDetailHostActivity.this).create();
        alertDialog.setTitle("Help");
        alertDialog.setMessage("To use this app please click on any of the articles listed on the left" +
                " hand side to view the article in the main viewer. If you would like to save the article" +
                " to your favourites, please hit the 'save' button if you would like to remove the article" +
                " from your favourites please hit the 'delete' button. If you would like to load the article" +
                " in a browser please hit the 'load' button. If you are enjoying the app please rate us" +
                " by clicking on the little star icon under the help icon.");
        alertDialog.setButton(AlertDialog.BUTTON_NEUTRAL, "OK",
                new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.dismiss();
                    }
                });
        alertDialog.show();

    });


    ImageButton favouritesList = findViewById(R.id.goToFavouritesButton);
    favouritesList.setOnClickListener(clk -> {
        Intent intent = new Intent(this, SoccerFavouritesList.class);
        startActivity(intent);

    });


    Toolbar toolbar = findViewById(R.id.toolbar);
    setSupportActionBar(toolbar);
    DrawerLayout drawer = findViewById(R.id.soccerDrawerLayout);
    ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(this, drawer, toolbar, R.string.nav_open, R.string.nav_close);
    drawer.addDrawerListener(toggle);
    toggle.syncState();

    NavigationView navView = findViewById(R.id.soccerSideMenu);
    navView.setNavigationItemSelectedListener((item) -> {
        onOptionsItemSelected(item);
        drawer.closeDrawer(GravityCompat.START);
        return false;
    });

}

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.menuactions, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch(item.getItemId()){
            case R.id.ocTranspoApp:
                Intent ocTranspoApp = new Intent(getApplicationContext(), OCTranspoActivity.class);
                startActivity(ocTranspoApp);
                onStop();
                break;
            case R.id.carApp:
                Intent carApp = new Intent(getApplicationContext(), CarActivity.class);
                startActivity(carApp);
                onStop();
                break;
            case R.id.movieApp:
                Intent movieApp = new Intent(getApplicationContext(), MovieActivity.class);
                startActivity(movieApp);
                onStop();
                break;
            case R.id.soccerApp:
                Intent soccerApp = new Intent(getApplicationContext(), SoccerItemDetailHostActivity.class);
                startActivity(soccerApp);
                onStop();
                break;
        }
        return super.onOptionsItemSelected(item);
    }
    

}

这是屏幕左侧的 ItemList 片段:

/**
 * This fragment combines with the NavController. It shows the list of articles on the left hand side
 */
public class SoccerItemListFragment extends Fragment {


private FragmentSocceritemListBinding binding;

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

    binding = FragmentSocceritemListBinding.inflate(inflater, container, false);
    return binding.getRoot();

}

@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);



    RecyclerView recyclerView = binding.socceritemList;

    /**
     * Finding the layout for this fragment container
     */
    View itemDetailFragmentContainer = view.findViewById(R.id.socceritem_detail_nav_container);

    /* Click Listener to trigger navigation based on if you have
     * a single pane layout or two pane layout
     */
    View.OnClickListener onClickListener = itemView -> {
        SoccerAPI.articles item =
                (SoccerAPI.articles) itemView.getTag();
        Bundle arguments = new Bundle();
        arguments.putString(SoccerItemDetailFragment.ARG_ITEM_ID, item.id);
        if (itemDetailFragmentContainer != null) {
            Navigation.findNavController(itemDetailFragmentContainer)
                    .navigate(R.id.fragment_socceritem_detail, arguments);
        } else {
            Navigation.findNavController(itemView).navigate(R.id.show_socceritem_detail, arguments);
        }


    };

    /*
     * This lets you see the information from each  article on the bigger part of the screen
     */
    View.OnContextClickListener onContextClickListener = itemView -> {
        SoccerAPI.articles item =
                (SoccerAPI.articles) itemView.getTag();
        Toast.makeText(
                itemView.getContext(),
                "Context click of item " + item.id,
                Toast.LENGTH_LONG
        ).show();
        return true;
    };

    setupRecyclerView(recyclerView, onClickListener, onContextClickListener);
}

/**
 * Sets up the recycler view where the articles will be populated. The dummy content is there for now
 * @param recyclerView
 * @param onClickListener
 * @param onContextClickListener
 */

private void setupRecyclerView(
        RecyclerView recyclerView,
        View.OnClickListener onClickListener,
        View.OnContextClickListener onContextClickListener
) {

    recyclerView.setAdapter(new SimpleItemRecyclerViewAdapter(
            SoccerAPI.ITEMS,
            onClickListener,
            onContextClickListener
    ));
}

@Override
public void onDestroyView() {
    super.onDestroyView();
    binding = null;
}

/**
 * Setting up the adapter for the recyclerview
 */
public static class SimpleItemRecyclerViewAdapter
        extends RecyclerView.Adapter<SimpleItemRecyclerViewAdapter.ViewHolder> {

    private final List<SoccerAPI.articles> mValues;
    private final View.OnClickListener mOnClickListener;
    private final View.OnContextClickListener mOnContextClickListener;

    SimpleItemRecyclerViewAdapter(List<SoccerAPI.articles> items,
                                  View.OnClickListener onClickListener,
                                  View.OnContextClickListener onContextClickListener) {
        mValues = items;
        mOnClickListener = onClickListener;
        mOnContextClickListener = onContextClickListener;
    }

    /**
     * Setting up the viewHolder
     * @param parent
     * @param viewType
     * @return
     */

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

        SocceritemListContentBinding binding =
                SocceritemListContentBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);
        return new ViewHolder(binding);

    }

    /**
     * Setting up the onBindViewHolder
     * @param holder
     * @param position
     */

    @Override
    public void onBindViewHolder(final ViewHolder holder, int position) {
        holder.mIdView.setText(mValues.get(position).id);
        holder.mContentView.setText(mValues.get(position).content);

        holder.itemView.setTag(mValues.get(position));
        holder.itemView.setOnClickListener(mOnClickListener);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            holder.itemView.setOnContextClickListener(mOnContextClickListener);
        }
    }

    /**
     * Getting the item count from the array so it knows how many to show on screen
     * @return
     */

    @Override
    public int getItemCount() {
        return mValues.size();
    }

    class ViewHolder extends RecyclerView.ViewHolder {
        final TextView mIdView;
        final TextView mContentView;

        ViewHolder(SocceritemListContentBinding binding) {
            super(binding.getRoot());
            mIdView = binding.idText;
            mContentView = binding.content;
        }

    }
}
}

这是右侧的详细片段:

 /**
 * This fragment displays the content from the array in SoccerAPI and displays the information in the bigger part of the
* screen (when in Tablet/Landscape mode).
 */
public class SoccerItemDetailFragment extends Fragment {

/**
 * The fragment argument representing the item ID that this fragment
 * represents.
 */
public static final String ARG_ITEM_ID = "item_id";

/**
 * Setting the content from the articles method in the SoccerAPI class to a variable
 */
private SoccerAPI.articles mItem;
private FragmentSocceritemDetailBinding binding;

/**
 * empty constructor
 */
public SoccerItemDetailFragment() {
}

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    /**
     * Load the data from the dummy content
     */

    if (getArguments().containsKey(ARG_ITEM_ID)) {

        mItem = SoccerAPI.ITEM_MAP.get(getArguments().getString(ARG_ITEM_ID));
    }
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {

    binding = FragmentSocceritemDetailBinding.inflate(inflater, container, false);
    View rootView = binding.getRoot();



    /**
     * Show the dummy content as text in a TextView & in the toolbar if available.
     */

    if (mItem != null) {
        TextView textView = binding.socceritemDetail;
        textView.setText(mItem.details);

    }

    return rootView;
}

@Override
public void onDestroyView() {
    super.onDestroyView();
    binding = null;
}
}

Executor 将在后台或主线程之外帮助您 运行 任务,要实际获取 rss 数据,您必须使用像 retrofit or volley. There are several tutorials available on how to get this done, like this one: https://www.vogella.com/tutorials/Retrofit/article.html[=11= 这样的库]