android - global layout refresh function - java

EDIT 2:
In my MainActivity I have a function (displayData) that displays the substitutions for my school.
public void displayData(List<Schoolday> results) {
TabLayout mainTabLayout = (TabLayout) findViewById(R.id.mainTabLayout);
ViewPager mainViewPager = (ViewPager) findViewById(R.id.mainViewPager);
// draw the tabs depending on the days from the file
mainTabLayout.removeAllTabs();
for (int n = 0; n < results.size(); n++) {
List<Subject> subjectsToDisplay = new ArrayList<>();
if (myPreferences.getBoolean(SHOW_WHOLE_PLAN, true)) {
subjectsToDisplay = results.get(n).getSubjects();
} else {
List<Subject> tempSubjectsToDisplay;
tempSubjectsToDisplay = results.get(n).getSubjects();
for (int i = 0; i < tempSubjectsToDisplay.size(); i++) {
if (tempSubjectsToDisplay.get(i).getCourse().contains(myPreferences.getString(CLASS_TO_SHOW, "None"))) {
subjectsToDisplay.add(tempSubjectsToDisplay.get(i));
}
}
}
results.get(n).setSubjects(subjectsToDisplay);
// only create a tab if there's any information to show within that tab
if (results.get(n).getSubjects().size() > 0) {
mainTabLayout.addTab(mainTabLayout.newTab().setText(results.get(n).getDate().substring(0, 6)));
}
}
PagerAdapter pagerAdapter = new PagerAdapter(getSupportFragmentManager(), mainTabLayout.getTabCount(), results);
mainViewPager.setAdapter(pagerAdapter);
mainViewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(mainTabLayout));
mainTabLayout.setOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
#Override
public void onTabSelected(TabLayout.Tab tab) {
mainViewPager.setCurrentItem(tab.getPosition());
}
#Override
public void onTabUnselected(TabLayout.Tab tab) {
}
#Override
public void onTabReselected(TabLayout.Tab tab) {
}
});
}
This is my main layout:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/main_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<android.support.design.widget.TabLayout
android:id="#+id/mainTabLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorPrimary"
android:elevation="6dp"
android:minHeight="?attr/actionBarSize"
android:theme="#style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:tabGravity="fill" />
<android.support.v4.view.ViewPager
android:id="#+id/mainViewPager"
android:layout_width="match_parent"
android:layout_height="fill_parent"
android:layout_below="#id/mainTabLayout" />
</RelativeLayout>
And this is the layout of the fragment that represents a page in the ViewPager:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v4.widget.SwipeRefreshLayout
android:id="#+id/mainSwipeContainer"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="#+id/mainRecyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true" />
</android.support.v4.widget.SwipeRefreshLayout>
</RelativeLayout>
Whenever a refresh is triggered by the SwipeRefreshLayout I need the function displayData to be called.
How can I call this function since the call comes from within a fragment/page from the ViewPager and displayData is not static?
Old question:
I've been searching around quite a while but couldn't find an answer. Maybe I've searched for the wrong thing - just pointing me to that would already help.
My project (Vertretungsplan on github) displays the substitution plan for my school which is available to view/download as an xml file (xml file from school website). I then display the data in my app.
I have a TabLayout (different tabs represent different days) and a connected ViewPager. Each page is using a fragment. Each fragment includes a RecyclerView (to display the results) which is wrapped in a SwipeRefreshLayout.
When I want to refresh the data with the SwipeRefreshLayout I need to download all the data again and then update all the pages as well as the TabLayout(a new day might have a been added so a new tab will be needed). Since my refresh happens inside a fragment but I'm referencing the TabLayout as well as the ViewPager from my MainActivity I have no clue how to properly access all the elements in order to update the content.
My idea was to set up a Broadcast to let my MainActivity know that it needs to refresh the page since it originally set up the whole layout but maybe there is a better solution for that?
I'm kinda new to stackoverflow so feel free to correct me in the way of asking things here! If there's anything other information you need just ask me!
I appreciate your help!!

There are a couple ways to implement what you are asking. The most direct way is to create an interface for the Activity that the Fragment uses. See the docs here:
http://developer.android.com/training/basics/fragments/communicating.html
The other option, which decouples the Fragment from the Activity (which some may see as an advantage) is to use an Intent. With that approach, you need to look into the onNewIntent method, as it is not obvious exactly how it works.
Either one should be fine, especially since you are exploring ideas at this point.

Related

Save ui elements in activity state

I am totaly new to java and android. I have the following UI setup:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<EditText
android:id="#+id/textView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
...
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<ScrollView
android:id="#+id/scrollView"
android:layout_width="match_parent"
android:layout_height="0dp"
...>
<LinearLayout
android:id="#+id/linLay"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" />
</ScrollView>
</androidx.constraintlayout.widget.ConstraintLayout>
During runtime, some TextViews will be added to the LinearLayout. I have following problem. The phone filps, the dynamicaly added TextViews disapeare. The reason ist that the onCreate(Bundle savedInstanceState) method of the activity is called, if you flip your phone.
I setup the following method, to save the instance state (textViews is a member of the Activity class (Vector)):
protected void onSaveInstanceState(Bundle state) {
super.onSaveInstanceState(state);
//Save the TextViews
textViews.removeAllElements();
LinearLayout layout = getLinearLayout();
while (layout.getChildCount() > 0)
{
View view = layout.getChildAt(0);
if (view instanceof TextView)
{
layout.removeView(view);
textViews.add((TextView) view);
}
}
state.putSerializable("textViews", textViews);
}
I implemented this cod in the onCreate(Bundle savedInstanceState) method to restore the TextViews:
LinearLayout linLay = getLinearLayout();
//Load TextViews
if ((savedInstanceState != null) && (savedInstanceState.getSerializable("textViews") != null))
{
Serializable serializable = savedInstanceState.getSerializable("textViews");
if (serializable instanceof Vector)
{
textViews= (Vector<TextView>) serializable;
}
}
if (textViews!= null) {
for (TextView textView : textViews) {
linLay.addView(textView);
}
}
This code works for the fliping issue, but it creates a new issue. If I press the home button, I get the following error:
Parcel: unable to marshal value
I read that the problem is that Vector<TextView> is not serializable. But why does fliping the phone does not effect this error? I debugt the app and I am sure, the state.putSerializable(...) method is called, if the phone is fliped.
How would you store a TextView instead? It is not possible to save a String instead of the TextView, because the backgroundResource of the TextViews differ.
I know that this int solution, but hear me out. TextViews are bunch of text on the screen right? my advice would be when activity recreation will happen dont save TextViews as themselves save text thats in the TextView. and after that use them to draw new ones. this is more logical, more optimized and by using ViewModel it can become more elegant by storing that texts in LiveData of List and observing and reacting in activity. here is documentation for it: LiveData and ViewModel

Inflated xml Layout with a TextView has an onClick attribute, but it does not get called.

I have an XML layout file that has a TextView within a CoordinatorLayout.
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:tools="http://schemas.android.com/tools"
tools:context=".SpecificProgramSelectionActivity">
<TextView
android:layout_width="match_parent"
android:layout_height="75dp"
android:id="#+id/saved_program"
android:text="Empty"
android:textAlignment="center"
android:gravity="fill"
android:textSize="20dp"
android:background="#drawable/program_selection_border"
android:textColor="#color/black"
android:clickable="true"
android:focusable="true"
android:onClick="addToSavedPrograms"
android:paddingTop="5dp"
android:paddingBottom="5dp"/>
And this code that inflates the layout and adds it into a Linear Layout in the activity's view.
for (PlayerWithObjectives player : players){
name = player.getName();
for (String objective : player.getObjectives()){
objectives.add(objective);
}
nameView = inflater.inflate(R.layout.inflatable_player_name_view, null);
((TextView)nameView.findViewById(R.id.saved_program)).setText(name);
((TextView)nameView.findViewById(R.id.saved_program)).setTextAlignment(View.TEXT_ALIGNMENT_CENTER);
((TextView)nameView.findViewById(R.id.saved_program)).setTextSize(20);
linearLayout.addView(nameView);
}
(This is the activity's layout XML)
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".SpecificProgramSelectionActivity"
android:orientation="vertical">
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:id="#+id/specific_program_selection_linear_layout">
</LinearLayout>
</ScrollView>
Everything looks fine in the app when I run it. Every inflated view shows up, the only issue is that the method that I specified in the onClick attribute for the inflated TextView does not get called. Why is that? Here is the method that is supposed to be called
public void addToSavedPrograms(View view){
String name = (String) (((TextView)view.findViewById(R.id.saved_program)).getText());
namesToSend.add(name);
editor.putStringSet("Tracked Players", namesToSend);
editor.commit();
Toast.makeText(getApplicationContext(),name + " was saved to preferences.", Toast.LENGTH_SHORT);
}
Why doesn't the method get called? I already saw all the other threads about using setContent() and stuff but that didn't work and it was not explained that great in the answer. Any help would be greatly appreciated.
public class YourActivity extends AppCompatActivity implements OnClickListener{
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView textview=(TextView)findViewById(R.id.saved_program);
textview.setOnClickListener(this);
}
#Override
public void onClick(View view) {
switch(view.getId()){
case R.id.saved_program:
//call your function here
default:
break;
}
}
you can do it like this.
Update:
First off, thank you to everybody that commented, your help was greatly appreciated. Your no judgment assistance is an awesome and welcome change from a lot of what I have experienced and seen on in this community.
Secondly, I figured out my issue. Or got my code to work at least. Once I got a better understanding of inflating layouts I changed this line of code
nameView = inflater.inflate(R.layout.inflatable_player_name_view, null);
To
nameView = (TextView) inflater.inflate(R.layout.inflatable_add_player_name_view, linearLayout, false);
The difference is that When you inflate, it returns an object that is the same type as the top parent in the specified layout file (Which in this case is "R.layout.inflatable_player_name_view"). So I changed nameView to a TextView object and cast the returned object as a TextView. Then I specified the wanted parent layout (To get the right layoutParams) and put false so that it does not attach it automatically to that parent layout. After that I simply made the alterations to the textView that I wanted, like setting the text values and whatnot, and then manually added it to the parent linearlayout that I wanted. After doing this there was not even a need to set an onClickListener programmatically because the android:onClick method worked just fine.

How can I set an activity to a button click variable that is only assigned via TapBarMenu with String as web and an image item as ImageId?

I am new to Android development and I have been looking through Q's/Documentation but unable to find direction on what my end game is.
I am building what could be described as a 'content aggregator' and at current I am struggling to see how I can set an activity that proceeds a click on each position to instigate either an Instagram profile mWebView or an API call. Currently my InstagramList.java is as follows -
public class InstagramList extends Activity {
ListView list;
String[] web = {
"Kyary Pamyu Pamyu",
"Tokyo Girls' Style",
"Haruka Nakagawa",
"Nemu Yumemi",
"Moga Mogami",
"Ayane Fujisaki",
"Koda Kumi",
"Atsuko Maeda",
"Tomomi Itano",
"Haruna Kojima",
"Utada Hikaru",
"Shibasaki Ko",
"Taeyon",
"Tiffany",
"Jessica",
"Sooyoung",
"Sunny",
"Laboum",
"YeEun",
"Yubin",
"Hyelim"
};
Integer[] imageId = {
R.drawable.kyary,
R.drawable.tgs,
R.drawable.haruka,
R.drawable.nemu,
R.drawable.moga,
R.drawable.ayane,
R.drawable.koda,
R.drawable.atsuko,
R.drawable.tomomi,
R.drawable.haruna,
R.drawable.utada,
R.drawable.shibasaki,
R.drawable.taeyon,
R.drawable.tiffany,
R.drawable.jessica,
R.drawable.sooyoung,
R.drawable.sunny,
R.drawable.laboum,
R.drawable.yeeun,
R.drawable.yubin,
R.drawable.hyelim
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.instagram_main);
CustomList adapter = new
CustomList(InstagramList.this, web, imageId);
}
}
The xml for this is as follows -
<?xml version="1.0" encoding="utf-8"?>
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<TableRow>
<ImageView
android:id="#+id/img"
android:layout_width="50dp"
android:layout_height="50dp" />
<TextView
android:id="#+id/txt"
android:layout_width="wrap_content"
android:layout_height="50dp" />
</TableRow>
</TableLayout>
Any pointers on how best to either modify this to allow better control of these variables or on a possible solution would be greatly received. Apologies if this has been asked in a similar vein before.
I observed that you are using two arrays. In place of this you can create a class with one member as string (web) and other as int (imageId). In addition to this you can add one variable for possible action as you mention there can be different actions. In adapter you can add tag on view and click listener so you can perform different actions on different item click.
Hope I understand your problem correctly. Let me know if you need more detail.
Check out this. In onItemClick method in MainActivity do what you want.

Android ViewPager not Showing

I am trying to make a little app to try out some things for a bigger app I am developing. I want to add a viewpager that displays four images, I developed this viewpager alone and it worked, but when I tried to add it to my other app (with just a couple of buttons) with the tag, it does not show anything and I am really stuck in here. Is possible to add the Viewpager this way? Honestly I don't have that much experience in Java, I usually program only C and Assembler and I will be reaaally glad if you could help me.
This is the xml file with the button and the include:
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
android:layout_height="match_parent" android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
android:paddingBottom="#dimen/activity_vertical_margin" tools:context=".MainActivity"
android:weightSum="1">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Calculo Rapido"
android:id="#+id/btcr"
android:layout_gravity="center_vertical"
android:layout_weight="0.16"
android:layout_alignParentStart="true" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Calculo Avanzado"
android:id="#+id/button"
android:layout_centerVertical="true"
android:layout_below="#+id/btcr" />
<include layout="#layout/viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerHorizontal="true"
android:translationZ="15dp" />
Now, the MainActivity, which holds the button activity because I kept the Viewpager inside an Activity that's linked with the viewpager original layout
Button siguiente;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
siguiente = (Button)findViewById(R.id.btcr);
siguiente.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent siguiente = new Intent(MainActivity.this, Main2Activity.class);
startActivity(siguiente);
}
});
}
And lastly, the code for the mainpager main activity, this uses a CustomPagerAdapter saved in another java class. As I told before, this viewpager does work alone, but when I try to implement it here, with the /include tag, it doesn't show anything.
public class MainActivity extends Activity {
CustomPagerAdapter mCustomPagerAdapter;
ViewPager mViewPager;
private static int currentPage = 0;
private static int NUM_PAGES = 0;
Button siguiente;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// == Setting up the ViewPager ==
mCustomPagerAdapter = new CustomPagerAdapter(this);
mViewPager = (ViewPager) findViewById(R.id.pager);
mViewPager.setAdapter(mCustomPagerAdapter);
final Handler handler = new Handler();
final Runnable Update = new Runnable() {
public void run() {
if (currentPage == NUM_PAGES) {
currentPage = 0;
}
mViewPager.setCurrentItem(currentPage++);
if (currentPage == 5) {
currentPage = 0;
mViewPager.setCurrentItem(currentPage++, false);
}
}
};
Timer swipeTimer = new Timer();
swipeTimer.schedule(new TimerTask() {
#Override
public void run() {
handler.post(Update);
}
}, 2000, 2000);
}
Sorry if it is too long guys, but I am a little frustrated, I have tried a lot of things but it isn't working yet. Either the buttons do their thing and the ViewPager doesn't work or the ViewPager works but the buttons are dead.
I will reaaally aprecciate your help :)
create a new xml file inside layout folder and name it secondactivity.xml
and put this code
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v4.view.ViewPager
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/pager">
</android.support.v4.view.ViewPager>
</LinearLayout>
Now inside your Main2Activity.class
change
setContentView(R.layout.activity_main);
to
setContentView(R.layout.secondactivity);
And remove the include from your previous xml
Turns out, what I needed was just the simple solution. I had to put the viewpager on my activity's layout file as another object, just configuring its size and position. And then, on my onCreate I pasted the code of my functioning ViewPager. Quite simple, without requiring another layouts :)

ViewHolder views flash/blink OnBind

Issue
Every time I call the OnBind() method inside my holders, views within them will flash or blink or maybe even fade.
I'm just pretty lost as to why, I've tried a number of things.
Changing them to have null as a background or have them all be a solid color.
Making sure nothing is gone at any point.
Removed any animations.
Asked Google a thousand times, a thousand ways.
Searched on here.
Thoughts
I've always thought that I would have to eventually try and hide it with animations and styling. Seeing it as just a side effect of the binding.
This is part of a larger project, obviously. So I still have to do all the animations, that's why I haven't done it for this yet. Figured I'd just ask and see if anyone could help find the solution another way in the meantime.
Any help, tips or advice would be great. Hopefully the code links work and are build able for everyone.
Thanks,
Jon.
Code
I'm not sure how much code I need to add in here, so I'll go small at first. If more should be added please let me know and I will. However I put it all on Github and Dropbox(Example Apk & Zip).
Links are at the bottom.
HeaderHolder.java
public class HeaderHolder extends BaseHolder {
#Bind(R.id.header_title_text)
TextView _titleTextView;
#Bind(R.id.header_status_image)
ImageView _statusImageView;
#BindDrawable(R.drawable.ic_selected)
Drawable _statusSelected;
#BindDrawable(R.drawable.ic_non_selected)
Drawable _statusNonselected;
private Header _header;
public HeaderHolder(View root, HolderCallBacks callbacks) {
super(null, root, callbacks);
}
#Override
public void OnBind(Base model) {
this._header = (Header) model;
String n = model._name();
this._titleTextView.setText(n);
this._statusImageView.setImageDrawable(this._header._iconset()._selected()
? this._statusSelected : this._statusNonselected);
}
#OnClick(R.id.header_item_wrapper)
public void _headerClick(View view) {
this._callbacks.OnHolderClick(view, this._header);
}
}
IconsetHolder.java
public class IconsetHolder extends BaseHolder {
#Bind(R.id.iconset_icon_recycler)
RecyclerView _iconsRecycler;
private AdapterCallBacks _adapterCallbacks;
public IconsetHolder(Context context, View root, AdapterCallBacks callbacks) {
super(context, root, null);
this._adapterCallbacks = callbacks;
}
#Override
public void OnBind(Base model) {
Iconset i = (Iconset) model;
this._iconsRecycler.setLayoutManager(new GridLayoutManager(
this._context, i._span(), GridLayoutManager.HORIZONTAL, false));
this._iconsRecycler.setAdapter(new ModelsAdapter(i._icons(), this._adapterCallbacks));
}
}
item_header.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/header_item_wrapper"
android:layout_width="wrap_content"
android:layout_height="56dip"
android:background="#595959"
tools:context=".views.adapters.holders.HeaderHolder">
<TextView
android:id="#+id/header_title_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="16dp"
android:textColor="#fff8f8f8"
android:gravity="center_vertical"
android:text="ICONSET"
android:textSize="24sp"
android:layout_centerVertical="true" />
<ImageView
android:id="#+id/header_status_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="12dp"
android:gravity="center_vertical"
android:layout_alignParentRight="true"
android:layout_centerVertical="true" />
</RelativeLayout>
item_iconset.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/iconset_item_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#null"
tools:context=".views.adapters.holders.IconsetHolder">
<android.support.v7.widget.RecyclerView
android:id="#+id/iconset_icon_recycler"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clipToPadding="false"
android:background="#595959"
android:layout_marginBottom="16dp"
/>
</RelativeLayout>
Example Links Removed
RecyclerView has some built in animations to it, using the DefaultItemAnimator. Specifically when you call notifiyItemChanged() it does a fade animation for the changing of the data in the ViewHolder. If you would like to disable this you can use the following:
RecyclerView.ItemAnimator animator = recyclerView.getItemAnimator(); // your recycler view here
if (animator instanceof DefaultItemAnimator) {
((DefaultItemAnimator) animator).setSupportsChangeAnimations(false);
}
This will disable the item changed animation (the fade you are seeing).

Categories