I am trying to implement an article viewer, which, at first, will display a list of articles divided by category with the use of sliding tabs and a ViewPager.
I am pulling the articles from a server and am trying to load them to the UI via a ContentProvider and a CursorLoader.
In order to implement the aforementioned structure I followed this which uses tabs with a ViewPager.
Here is the point of interest:
#Override
public Object instantiateItem(ViewGroup container, int position) {
// Inflate a new layout from our resources
View view = getActivity().getLayoutInflater().inflate(R.layout.article_list,
container, false);
// Add the newly created View to the ViewPager
container.addView(view);
switch (position) {
case 0:
Intent intent1 = new Intent(getActivity(), ArticleService.class);
intent1.putExtra(ArticleService.CATEGORY_EXTRA, 1);
getActivity().startService(intent1);
break;
case 1:
Intent intent2 = new Intent(getActivity(), ArticleService.class);
intent2.putExtra(ArticleService.CATEGORY_EXTRA, 2);
getActivity().startService(intent2);
break;
case 2:
Intent intent3 = new Intent(getActivity(), ArticleService.class);
intent3.putExtra(ArticleService.CATEGORY_EXTRA, 3);
getActivity().startService(intent3);
break;
case 3:
Intent intent4 = new Intent(getActivity(), ArticleService.class);
intent4.putExtra(ArticleService.CATEGORY_EXTRA, 4);
getActivity().startService(intent4);
break;
case 4:
Intent intent5 = new Intent(getActivity(), ArticleService.class);
intent5.putExtra(ArticleService.CATEGORY_EXTRA, 5);
getActivity().startService(intent5);
break;
}
// Return the View
return view;
}
As you can see I am creating a different request that corresponds to a different article category and I intend for the resulting content for each tab to be displayed in a ListView. The plan is to set the CursorAdapter to that ListView but I've never worked with sliding tabs before. How will I go about implementing that?
Thanks in advance.
Related
I have used a switch case in a RecyclerView to open different Activitys. It works perfectly when items are not filtered. But when the items are filtered, items show wrong Activitys. I think it happens because when items are filtered they get upper position. I don't know how to come over this issue.
public void onItemClick(int position) {
switch (position ){
case 0:
Intent pope = new Intent(this, Pope.class);
startActivity(pope);
break;
case 1:
Intent austin = new Intent(this, AlfredAustine.class);
startActivity(austin);
break;
case 2:
Intent housman = new Intent(this, AlfredHousman.class);
startActivity(housman);
break;
case 3:
Intent tennyson = new Intent(this, LordTennyson.class);
startActivity(tennyson);
break;
case 4:
Intent swinburn = new Intent(this, AlgernonSwinburn.class);
startActivity(swinburn);
break;
case 5:
Intent lang = new Intent(this, AndrewLang.class);
startActivity(lang);
break;
}
}
It looks like you are using a switch case on the item position. This can lead to problems when your adapter is modified. When you 'filter' your adapter (however you are implementing it), positions of the items can change and this will lead to your switch case not working as expected.
Consider using switch case on an item's parameter or if you must use position, store the position in your item object instead of relying on the adapter position.
This looks like a software design issue question. This is the way I would solve that. In the Adapter probably you have a list of models. Something like this:
List<MyModel> modelList = new ArrayList<>();
So in MyModel class you should define an id for the model. E.e:
public class MyModel {
#NonNull
private int id;
....
}
Then the switch will be something like:
public void onItemClick(int position) {
switch (models.get(position).getId()){
case POPE_ID:
Intent pope = new Intent(this, Pope.class);
startActivity(pope);
break;
case ALFRED_ID:
Intent austin = new Intent(this, AlfredAustine.class);
startActivity(austin);
break;
case ALFRED_HOUSMAN_ID:
Intent housman = new Intent(this, AlfredHousman.class);
startActivity(housman);
break;
.....
}
}
I created a Navigation Drawer and inserted the necessary items and their fragments.
Also, I added a item that I have to open another Activity, which code should I put that by pressing on the item do this?
For example, item id nav_menu6 ..... but it should not be linked to a fragment but open an activity ...
private void displaySelectedScreen(int itemId) {
Fragment fragment = null;
// Handle navigation view item clicks here.
switch (itemId) {
case R.id.nav_menu1:
fragment = new BlankFragment();
break;
case R.id.nav_menu2:
fragment = new BlankFragment2();
break;
case R.id.nav_menu3:
fragment = new BlankFragment3();
break;
case R.id.nav_menu4:
fragment = new BlankFragment4();
break;
case R.id.nav_menu5:
fragment = new BlankFragment5();
break;
case R.id.nav_menu6:
fragment = new BlankFragment6();
break;
}
https://developer.android.com/training/basics/firstapp/starting-activity.html
Read this about starting a new activity.
Basically, the key is the intent.
Intent intent = new Intent(this, YourActivityName.class);
startActivity(intent);
or in a shortened form:
startActivity(new Intent(this, YourActivityName.class));
I want to set a default frame layout on fragment,and this frame layout shows at top of the half layout,and remaining half contains list view.frame layout updates the layout item when click on the list item these all work fine.But when I want to set default frame layout when comes from previous activity then it creates problem.Here is how i am opening my fragments and this is my adapter class from this activity. I want to set the default frame layout on next activity containing list.
grid.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View arg0) {
// Get the position
resultp = data.get(position);
frame=(FrameLayout)grid.findViewById(R.id.fragment_container);
Intent intent = new Intent(context, SingleItemView.class);
intent.putExtra("id", resultp.get(MainActivity.RANK));
intent.putExtra("count", resultp.get(MainActivity.COUNTRY));
intent.putExtra("population",resultp.get(MainActivity.POPULATION));
intent.putExtra("flag", resultp.get(MainActivity.FLAG));
// Start SingleItemView Class
intent.putExtra("mylist", data);
context.startActivity(intent);
if(frame==null){
DefaultLogFrag fragment1 = new DefaultLogFrag();
FragmentManager fragmentManager1 = ((Activity) context).getFragmentManager();
FragmentTransaction fragmentTransaction1 = fragmentManager1.beginTransaction();
fragmentTransaction1.add(R.id.fragment_container, fragment1);
fragmentTransaction1.commit();
}
}
});
Just Open your next activity using
Intent intent = new Intent(context, SingleItemView.class);
And add the Fragment transaction in onCreate() of SingleItemView.java activity
If you want to add different Fragments based on some condition then you can pass conditional variables with intent intent variable extra :
intent.putExtra("whichFragment", /* integer parameter */ 4);
Handle them in onCreate() of SingleItemView.java :
switch(getIntent().getExtraString("whichFragment")) {
case 1: /* add Fragment 1 */
break;
case 2: /* add Fragment 2 */
break;
case 3: /* add Fragment 3 */
break;
case 4: /* add Fragment 4 */
break;
}
So, I have 3 activities that I want to link with a Navigation drawer but I'm exactly sure how to do that. I saw somewhere that I should make a new class for the Navigation Drawer methods or something like that but I didn't really understand. So, what would be a good way to do this?
By the way, I'm very new to android development...
It is simple, if you check the navigation drawer of Google examples, they load a fragment when you click in an item.
Just change it, and use for each item:
Intent intent = new Intent(MainActivity.this, SecondActivity.class);
startActivity(intent);
Change the activities names for each one of your three activities.
Here an example of how to do it:
Navigation Drawer
In this part you have to change the code that I mentioned before:
/**
* Slide menu item click listener
* */
private class SlideMenuClickListener implements
ListView.OnItemClickListener {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position,
long id) {
// display view for selected nav drawer item
displayView(position);
}
}
/**
* Diplaying fragment view for selected nav drawer list item
* */
private void displayView(int position) {
// update the main content by replacing fragments
Fragment fragment = null;
switch (position) {
case 0:
Intent intent = new Intent(MainActivity.this, SecondActivity.class);
startActivity(intent);
break;
case 1:
Intent intent = new Intent(MainActivity.this, ThirdActivity.class);
startActivity(intent);
break;
case 2:
Intent intent = new Intent(MainActivity.this, SecondActivity.class);
startActivity(intent);
break;
case 3:
Intent intent = new Intent(MainActivity.this,ForthActivity.class);
startActivity(intent);
break;
default:
break;
}
As far as I know, you should make a new activity in which the navigation-drawer is used, and then convert the 3 activities to fragments. In this way, you can navigation between this 3 fragments, This is the recommended pattern to navigate between some Top level "View".
Please keep in mind I'm a beginner in Android, so it is possible that somewhere I overlooked something. Also I have more then one question here, because my approach to the whole project might be wrong.
I have an application that has side menu implemented via DrawerLayout. The "main screen" is a Fragment and with Drawer I create the side menu with a list in it. After that this is how I change the fragments upon clicking on the side menu:
private void displayView(int position) {
// update the main content by replacing fragments
Fragment fragment = null;
switch (position) {
case 0:
fragment = new HomeFragment();
break;
case 1:
fragment = new FavoritesFragment();
break;
case 2:
fragment = new LastVisitedFragment();
break;
case 3:
fragment = new SettingsFragment();
break;
default:
break;
}
if (fragment != null) {
FragmentManager fragmentManager = getFragmentManager();
fragmentManager.beginTransaction().replace(R.id.frame_container, fragment).commit();
// update selected item and title, then close the drawer
mDrawerList.setItemChecked(position, true);
mDrawerList.setSelection(position);
setTitle(navMenuTitles[position]);
mDrawerLayout.closeDrawer(mDrawerList);
} else {
Log.e("MainActivity", "Error in creating fragment");
}
}
The code above is implemented in my main activity. Next this is how my HomeFragment looks:
(The home fragment is my home page so to say)
public class HomeFragment extends Fragment {
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Log.i("HomeFragment", "OK");
View rootView = inflater.inflate(R.layout.fragment_home, container, false);
rootView.setOnClickListener(new View.OnClickListener() {
#
Override
public void onClick(View v) {
Log.i("HomeFragment", "OK0");
switch (v.getId()) {
case R.id.rec_prod1:
Log.i("HomeFragment", "OK1");
Intent intent1 = new Intent(getActivity(), SingleItemActivity.class);
startActivity(intent1);
break;
case R.id.rec_prod2:
Log.i("HomeFragment", "OK2");
Intent intent2 = new Intent(getActivity(), SingleItemActivity.class);
startActivity(intent2);
break;
case R.id.rec_prod3:
Log.i("HomeFragment", "OK3");
Intent intent3 = new Intent(getActivity(), SingleItemActivity.class);
startActivity(intent3);
break;
}
}
});
return rootView;
}
}
I have tried it with onTouchListener to but didn't work either, the single difference was that with onTouch it entered in to the onTouch method, bet nothing happened after. Also somewhere on this site I have red to set the scrollview clickable=true and all childs to false in order for it to work, but that didn't do either.
Also fragment_home.xml is a custom layout with the Parent being a ScrollView (It's a list with three coloumns and 5 rows but each element is different, they all have an Imageview and different number of textviews, I didn't use a ListView or GridList since each row has a title, and the elements in each row have different layouts) I attach only an overview of it since the real xml code is over 800 lines long.
<ScrollView>
<LinearLayout>
<!--This part repeats 5 times creating each row -->
<TextView>
<LinearLayout>
<!-- This part repeats 3 times for each element of a column -->
<RelativeLayout>
<ImageView>
<!-- This part changes acodringly to the number of textviews, I can have 1 to 3 TextViews here-->
<LinearLayout>
<TextView>
<TexytView>
</LinearLayout>
</RelativeLayout>
</LinearLayout>
</LinearLayout>
</ScrollView>
So I have two main questions.
1. Why doesn't the clicking/touching work.
2. Is the project structure(architecture) ok like this? The whole idea is that when a click happens some elements take me to a product view screen, while others to a list of product elements (the data that fills all this will come from a server, that's why I started a new Activity in the onClick method).
You either need to set an onclick action via XML (this works for activities), or you need to set it in code (almost like you have it) but assigned to the views themselves
Personally I prefer to define the click actions in the code. I think it makes much more sense when reading a project and you avoid any issues like this. It's also the recommended way to do it and is how most sample code and examples will do it (see Android Documentation)
To define the click via code do it like this:
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_home, container, false);
View OK3 = rootView.findViewById(R.id.rec_prod3); //pick a better name than i have though
if (OK3 != null) {
OK3.setOnClickListener(new View.OnclickListener() {
Log.i("HomeFragment", "OK3");
Intent intent3 = new Intent(getActivity(), SingleItemActivity.class);
startActivity(intent3);
});
}
Most examples use this method as the alternative of using a switch statement and directing every click through the same method is bad practice - essentially you are hacking a shortcut Google has provided for XML support into doing something it wasn't intended for. For more information as to why it is better to use a dedicated listener, look up Inheritance vs Composition - as a rule "Has A" always beats "Is A" for code readability and maintainability.
You're setting the OnClickListener for the whole fragment, not the actual views you are querying in the switch-statement. Of course the specific code parts are never reached!
This approach should work (and is officially supported by Google and well received by users on Stack Overflow):
public class HomeFragment extends Fragment implements View.OnClickListener {
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Log.i("HomeFragment", "OK");
View rootView = inflater.inflate(R.layout.fragment_home, container, false);
rootView.findViewById(R.id.rec_prod1).setOnClickListener(this);
rootView.findViewById(R.id.rec_prod2).setOnClickListener(this);
rootView.findViewById(R.id.rec_prod3).setOnClickListener(this);
return rootView;
}
#Override
public void onClick(View v) {
Log.i("HomeFragment", "OK0");
switch (v.getId()) {
case R.id.rec_prod1:
Log.i("HomeFragment", "OK1");
Intent intent1 = new Intent(getActivity(), SingleItemActivity.class);
startActivity(intent1);
break;
case R.id.rec_prod2:
Log.i("HomeFragment", "OK2");
Intent intent2 = new Intent(getActivity(), SingleItemActivity.class);
startActivity(intent2);
break;
case R.id.rec_prod3:
Log.i("HomeFragment", "OK3");
Intent intent3 = new Intent(getActivity(), SingleItemActivity.class);
startActivity(intent3);
break;
}
}
}
For further reading:
Setting the onClick via XML would also work, but sadly, only in activities, not in fragments: The method is invoked on the fragment's parent activity (see How to handle button clicks using the XML onClick within Fragments).
Still, the above approach has a disadvantage: onClick is public and thus could be called from outside. For example, the parent activity could call
((View.OnClickListener) homeFragment).onClick(
homeFragment.getView().findViewById(R.id.rec_prod1));
This would fire an onClick event for rec_prod1. IMO, the readability and convenience this approach brings outweighs this. Of course, there are other approaches with their own advantages and disadvantages. Most importantly, to prevent this public access, you can create an anonymous class for each listener (see Nick's answer and its disadvantages) or use the code below instead:
public class HomeFragment extends Fragment {
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Log.i("HomeFragment", "OK");
View rootView = inflater.inflate(R.layout.fragment_home, container, false);
View.OnClickListener onClick = new RecProdClickListener();
rootView.findViewById(R.id.rec_prod1).setOnClickListener(onClick);
rootView.findViewById(R.id.rec_prod2).setOnClickListener(onClick);
rootView.findViewById(R.id.rec_prod3).setOnClickListener(onClick);
return rootView;
}
private class RecProdClickListener implements View.OnClickListener {
#Override
public void onClick(View v) {
Log.i("HomeFragment", "OK0");
switch (v.getId()) {
case R.id.rec_prod1:
Log.i("HomeFragment", "OK1");
Intent intent1 = new Intent(getActivity(), SingleItemActivity.class);
startActivity(intent1);
break;
case R.id.rec_prod2:
Log.i("HomeFragment", "OK2");
Intent intent2 = new Intent(getActivity(), SingleItemActivity.class);
startActivity(intent2);
break;
case R.id.rec_prod3:
Log.i("HomeFragment", "OK3");
Intent intent3 = new Intent(getActivity(), SingleItemActivity.class);
startActivity(intent3);
break;
}
}
}
}
Beside all that several anonymous click listeners will lead to more memory usage and a little bit longer intialization time, whereas reusing a click listener with a switch-statement will make performance a negligibly worse due to cyclomatic complexity.
But you can even mix the different approaches. Ultimately, it's up to your taste. Discussing what's best under what circumstances would belong to programmers.stackexchange and surely is an interesting topic for clean code in Android development.