I want to show a snackbar as soon as the user opens the Google Maps activity, but the thing is that there's no views in the activity to use as the first parameter of the activity (in the findViewById() of Snackbar.make()). What do I put there?
Here's the java class code:
public class MapsActivity extends FragmentActivity implements OnMapReadyCallback {
private GoogleMap mMap;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_maps);
// Obtain the SupportMapFragment and get notified when the map is ready to be used.
SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
.findFragmentById(R.id.map);
mapFragment.getMapAsync(this);
}
#Override
public void onMapReady(GoogleMap googleMap) {
mMap = googleMap;
mMap.setBuildingsEnabled(true);
mMap.getUiSettings().setZoomControlsEnabled(true);
float cameraZoom = 17;
LatLng location = new LatLng(43.404032, -80.478184);
mMap.addMarker(new MarkerOptions().position(location).title("49 McIntyre Place #18, Kitchener, ON N2R 1G3"));
CameraUpdateFactory.newLatLngZoom(location, cameraZoom);
Snackbar.make(findViewById(/*WHAT DO I PUT HERE?*/), "Click the pin for more options", Snackbar.LENGTH_LONG).show();
}
}
Also, here is the activity xml code:
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:map="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/map"
android:name="com.google.android.gms.maps.SupportMapFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="ca.davesautoservice.davesautoservice.MapsActivity" />
And lastly, here's the stacktrace error:
08-03 11:42:21.333 3901-3901/? E/AndroidRuntime: FATAL EXCEPTION: main
Process: ca.davesautoservice.davesautoservice, PID: 3901
java.lang.NullPointerException
at android.support.design.widget.Snackbar.<init>(Snackbar.java:183)
at android.support.design.widget.Snackbar.make(Snackbar.java:215)
at ca.davesautoservice.davesautoservice.MapsActivity.onMapReady(MapsActivity.java:48)
at com.google.android.gms.maps.SupportMapFragment$zza$1.zza(Unknown Source)
at com.google.android.gms.maps.internal.zzo$zza.onTransact(Unknown Source)
at android.os.Binder.transact(Binder.java:361)
at xz.a(:com.google.android.gms.DynamiteModulesB:82)
at maps.ad.u$5.run(Unknown Source)
at android.os.Handler.handleCallback(Handler.java:808)
at android.os.Handler.dispatchMessage(Handler.java:103)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:5333)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:828)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:644)
at dalvik.system.NativeStart.main(Native Method)
Thanks for the help! :)
I see some options... Not sure which one can fix your issue.
Simpliest
SupportMapFragment extends class android.support.v4.app.Fragment. This way, it has a method getView()
Snackbar.make(mapFragment.getView(), "Click the pin for more options", Snackbar.LENGTH_LONG).show();
Find Root View
From this answer, there's a way to get the root view via:
getWindow().getDecorView().getRootView()
So, maybe, you can do:
Snackbar.make(getWindow().getDecorView().getRootView(), "Click the pin for more options", Snackbar.LENGTH_LONG).show();
NOTE: This approach has the side effect mentioned in the comments below:
The message will be shown behind the bottom navigation bar if the following method will be used to get view getWindow().getDecorView().getRootView()
Add a dummy LinearLayout to get the View
Honestly, I'm not sure if this solution is possible. I'm not sure if you can add a LinearLayout above the Maps fragment... I think it is OK but since I never work with Maps API before, I'm not sure.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/dummy_layout_for_snackbar"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<fragment
xmlns:map="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/map"
android:name="com.google.android.gms.maps.SupportMapFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="ca.davesautoservice.davesautoservice.MapsActivity" />
</LinearLayout>
and then:
Snackbar.make(findViewById(R.id.dummy_layout_for_snackbar), "Click the pin for more options", Snackbar.LENGTH_LONG).show();
Try this in any activity:
snackbar(findViewById(android.R.id.content),"your text")
As Shahab Rauf points out, getting the view via getDecorView() may put the snackbar behind the navigation bar in the bottom. I use the following code: (Use and extend to your delight)
public class BaseActivity extends AppCompatActivity {
private View getRootView() {
final ViewGroup contentViewGroup = (ViewGroup) findViewById(android.R.id.content);
View rootView = null;
if(contentViewGroup != null)
rootView = contentViewGroup.getChildAt(0);
if(rootView == null)
rootView = getWindow().getDecorView().getRootView();
return rootView;
}
protected void showSnackBarWithOK(#StringRes int res) {
final View rootView = getRootView();
if(rootView != null) {
final Snackbar snackbar = Snackbar.make(getRootView(), res, Snackbar.LENGTH_INDEFINITE);
snackbar.setAction(R.string.ok, new View.OnClickListener() {
#Override
public void onClick(View v) {
snackbar.dismiss();
}
});
snackbar.show();
}
}
protected void showSnackBar(#StringRes int res) {
final View rootView = getRootView();
if(rootView != null)
Snackbar.make(rootView, res, Snackbar.LENGTH_LONG).show();
}
}
OK, So all above solutions are either too complex or irrelevant.
Simplest solution is to use this function ANYWHERE
fun showSnackBar(message: String?, activity: Activity?) {
if (null != activity && null != message) {
Snackbar.make(
activity.findViewById(android.R.id.content),
message, Snackbar.LENGTH_SHORT
).show()
}
}
Wanna call this function in Activity?
showSnackBar("Test Message in snackbar", this)
Wanna call this function in Fragment?
showSnackBar("Test Message in snackbar", requireActivity())
Happy Coding!
The simplest way of doing this is to get the advantage of Extention Function in Kotlin.
Step 1: Create a kotlin file i.e Extensions.kt and paste below code
For Activity:
fun Activity.showSnackBar(msg:String){
Snackbar.make(this.findViewById(android.R.id.content), msg, Snackbar.LENGTH_SHORT)
.setAction("Ok"){
}
.show()
}
For Fragment:
fun Fragment.showSnackBar(msg:String){
Snackbar.make(this.requireActivity().findViewById(android.R.id.content), msg, Snackbar.LENGTH_SHORT)
.setAction("Ok"){
}
.show()
}
Step 2: Call it like below:
showSnackBar("PASS HERE YOUR CUSTOM MESSAGE")
Don't do it.
Why ?
The entire idea of giving an anchor view to snackbar is probably because it gives us the ablity to put it on our favourite place on the screen,
As mentioned in other answers there are ways by which you can attach the root view to it automatically, Using getWindow().getDecorView().getRootView() is one of these, but it can cause this message to appear on bottom navigation view, which doesn't look good (at least to me).
If you want something for general message display go for toast, it is much less error prone and have a consistent appearence, if you need something with interaction I would suggest Alert Dialogue
I would say that findViewByIdis the best approach. As using getWindow().getDecorView().getRootView() will cause the message to be shown behind the bottom navigation bar.
In my case, I didn't need to create a dummy element in XML. I used the following code
Snackbar.make(findViewById(R.id.container),"This will start payment process.", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
and my XML file is as follows
<android.support.design.widget.CoordinatorLayout
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"
android:fitsSystemWindows="true"
tools:context=".HomePatientActivity">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="#dimen/appbar_padding_top"
android:theme="#style/AppTheme.AppBarOverlay">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:layout_weight="1"
android:background="?attr/colorPrimary"
app:layout_scrollFlags="scroll|enterAlways"
app:popupTheme="#style/AppTheme.PopupOverlay">
</android.support.v7.widget.Toolbar>
<android.support.design.widget.TabLayout
android:id="#+id/sliding_tabs"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
app:layout_anchorGravity="top"
app:tabMode="fixed" />
</android.support.design.widget.AppBarLayout>
<android.support.v4.view.ViewPager
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_behavior="#string/appbar_scrolling_view_behavior">
</android.support.v4.view.ViewPager>
<android.support.design.widget.FloatingActionButton
android:id="#+id/fab"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="end|bottom"
android:layout_margin="#dimen/fab_margin"
app:srcCompat="#android:drawable/ic_dialog_email" />
</android.support.design.widget.CoordinatorLayout>
Add any layout and assign the layout an id example below:
<RelativeLayout
android:id="#+id/relativeLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment
android:id="#+id/map_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:name="com.google.android.gms.maps.SupportMapFragment"
/>
</RelativeLayout>
In your activity find the layout using findViewById:
RelativeLayout relativeLayout = findViewById(R.id.relativeLayout);
Example for other layouts respectively as long as your assign the layouts their Id's:
LinearLayout relativeLayout = findViewById(R.id.linearLayout);
FrameLayout frameLayout = findViewById(R.id.frameLayout);
Displaying the Snackbar:
Snackbar.make(relativeLayout,"You are displaying a Snackbar",Snackbar.LENGTH_SHORT).show();
For other layouts just change the name of the layouts examples:
Snackbar.make(linearLayout,"You are displaying a Snackbar",Snackbar.LENGTH_SHORT).show();
Snackbar.make(frameLayout,"You are displaying a Snackbar",Snackbar.LENGTH_SHORT).show();
Use as extension function:
fun Activity?.showSnackBar(message: String) {
this ?: return
Snackbar.make(
this.findViewById(android.R.id.content),
message, Snackbar.LENGTH_LONG
).show()
}
With Jetpack Compose stable version released,now you no more need views in xml .
#Composable
fun Snackbar() {
Row {
if (viewModel.snackbarVisibleState.value) {
Snackbar(
action = {
Button(onClick = {
viewModel.snackbarText.value = ""
viewModel.snackbarVisibleState.value = false
}) {
Text("Ok")
}
},
modifier = Modifier
.padding(8.dp)
.align(Alignment.Bottom)
) {
Text(viewModel.snackbarText.value)
}
}
}
}
Here snackbarVisibleState is a MutableState inside viewmodel whose value you need to change in order to show and dismiss Snackbar.
For more code snippets for Jetpack Compose, refer this - https://androidlearnersite.wordpress.com/2021/08/03/jetpack-compose-1-0-0-sample-codes/
Related
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
I'm building a chat app in Android Studio with the abitily to send routes on a map. I've implemented this by using a RecyclerView and ViewHolders, which hold the necessary UI with MapViews in lite mode (map:liteMode="true"). Thing is, when I add a map UI element to the recyclerView and scroll to the end by using scrollToPosition(adapter.getItemCount() - 1), the scrolling has trouble following the views and is always slightly off, as seen on the screenshot
(https://i.postimg.cc/BvMzrHJL/Screenshot-20190526-005535.png).
Moreover, the keyboard gets confused about the height of the views as well when clicked (https://i.postimg.cc/Hs6BsHfR/Screenshot-20190526-011647.png).
I have tried switching lite mode off, but it makes the scrolling laggy and handling lifecycle events becomes an issue since my MapViews are in ViewHolders, not in Activities or Fragments, see official documentation:
Users of this class [MapView] must forward all the life cycle methods from the
Activity or Fragment containing this view to the corresponding ones in
this class.
I have also tried changing the height of the layout from android:layout_height="wrap_content" to android:layout_height="250dp", but that also didn't work at all.
Also, scrolling works just fine with Views containing only text or an empty RelativeLayout istead of MapView.
I used this sample from Google's developer documentation https://github.com/googlemaps/android-samples/blob/master/ApiDemos/java/app/src/main/java/com/example/mapdemo/LiteListDemoActivity.java
So here is my ViewHolder (one of two):
private class SentRouteViewHolder extends RecyclerView.ViewHolder implements OnMapReadyCallback
{
MapView sentMap;
TextView routeSentTime;
GoogleMap map;
public SentRouteViewHolder(#NonNull View itemView) {
super(itemView);
sentMap = itemView.findViewById(R.id.sent_map);
routeSentTime = itemView.findViewById(R.id.route_sent_time);
sentMap.onCreate(null);
sentMap.onResume();
sentMap.getMapAsync(this);
}
#Override
public void onMapReady(GoogleMap googleMap) {
MapsInitializer.initialize(getApplicationContext());
map = googleMap;
setMapRoute();
}
void bind(Message message)
{
sentMap.setTag(message);
setMapRoute();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("HH:mm");
routeSentTime.setText(simpleDateFormat.format(message.getTime()));
}
void setMapRoute()
{
if(map == null) return;
Message message = (Message) sentMap.getTag();
if(message==null) return;
map.setMapType(GoogleMap.MAP_TYPE_NORMAL);
}
}
And adding the item to the RecyclerView:
activeCollection.add(newMessage).addOnSuccessListener(documentReference -> {
documentReference.get().addOnSuccessListener(documentSnapshot -> {
adapter.addMessage(documentSnapshot);
adapter.notifyItemInserted(adapter.getItemCount());
chatReycler.scrollToPosition(adapter.getItemCount()-1);
});
});
The onBindViewHolder:
SentRouteViewHolder routeViewHolder = (SentRouteViewHolder) viewHolder;
routeViewHolder.bind(message);
The onCreateViewHolder:
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.route_sent,parent,false);
Log.v("measure",String.valueOf(v.getMeasuredHeight()));
return new SentRouteViewHolder(v);
RecyclerView configuration:
manager.setStackFromEnd(true);
chatReycler.setLayoutManager(manager);
chatReycler.setAdapter(adapter);
chatReycler.setHasFixedSize(false);
chatReycler.setRecyclerListener(viewHolder -> {
if(viewHolder.getItemViewType()==ChatRecyclerViewAdapter.VIEW_TYPE_ROUTE_RECEIVED)
{
ChatRecyclerViewAdapter.ReceivedRouteViewHolder holder = (ChatRecyclerViewAdapter.ReceivedRouteViewHolder) viewHolder;
if(holder.map!=null)
{
holder.map.clear();
holder.map.setMapType(GoogleMap.MAP_TYPE_NONE);
}
}
else if (viewHolder.getItemViewType()==ChatRecyclerViewAdapter.VIEW_TYPE_ROUTE_SENT)
{
ChatRecyclerViewAdapter.SentRouteViewHolder holder = (ChatRecyclerViewAdapter.SentRouteViewHolder) viewHolder;
if(holder.map!=null)
{
holder.map.clear();
holder.map.setMapType(GoogleMap.MAP_TYPE_NONE);
}
}
});
The ViewHolder XML file
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:tools="http://schemas.android.com/tools"
android:paddingBottom="3dp"
android:layout_marginBottom="13dp">
<ImageView
android:id="#+id/route_received_background"
android:layout_width="250dp"
android:layout_height="250dp"
android:layout_marginStart="15dp"
android:src="#drawable/message_received_background"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.gms.maps.MapView
xmlns:map="http://schemas.android.com/apk/res-auto"
android:id="#+id/received_map"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_margin="9dp"
app:layout_constraintBottom_toBottomOf="#+id/route_received_background"
app:layout_constraintEnd_toEndOf="#+id/route_received_background"
app:layout_constraintStart_toStartOf="#+id/route_received_background"
app:layout_constraintTop_toTopOf="#+id/route_received_background"
map:mapType="normal"
map:liteMode="true"
/>
<TextView
android:id="#+id/route_received_time"
style="#style/TextAppearance.MaterialComponents.Caption"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="6dp"
android:textSize="13sp"
app:layout_constraintBottom_toBottomOf="#+id/route_received_background"
app:layout_constraintStart_toEndOf="#+id/route_received_background"
tools:text="11:50" />
</android.support.constraint.ConstraintLayout>
I want the RecyclerView to scroll to the bottom of the sent map, and not to the middle of it. How can I make that happen?
I had a similar problem with scrolling and checked that the parent layout was 0dp high. I changed it to match_parent and it worked.
android:id="#+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
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.
Before any of you tell me that this has been answered many times before, let me clarify most of those who've asked this question either gave the wrong view id or set a different ContentView than where the TextView was located. I have already used this function in a different where it worked fine. I've tried using a string literal in the TextView.setText(), but it was futile.
contactstory.xml file
<?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">
<TextView
android:id="#+id/textyman"
android:layout_width="match_parent"
android:layout_height="300dp"
android:layout_marginTop="100dp"
android:text="TextView" />
<Button
android:id="#+id/close"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Close" />
</LinearLayout>
Function in java file
public void ShowStory(int pos)
{
final Dialog dialog = new Dialog(this);
dialog.setTitle(srcnames[pos]);
dialog.setContentView(R.layout.contactstory);
String username=getIntent().getStringExtra("username");
LoginDataBaseAdapter loginDataBaseAdapter=new
LoginDataBaseAdapter(this);
loginDataBaseAdapter.open();
String story=loginDataBaseAdapter.GetStory(pos,username);
TextView t1=(TextView)findViewById(R.id.textyman);
Button btnend=(Button)findViewById(R.id.close);
if(TextUtils.isEmpty(story)){
t1.setText(username+" hasn't added any story for this song");
}
else {
t1.setText(story); //Exception is thrown here
}
btnend.setOnClickListener(new View.OnClickListener(){
public void onClick(View v) {
dialog.dismiss();
}
});
dialog.show();
}
Logcat
FATAL EXCEPTION: main
java.lang.NullPointerException
at com.example.sherry.escuchame.ContactInfo.ShowStory(ContactInfo.java:157)
at
com.example.sherry.escuchame.ContactInfo.onContextItemSelected
(ContactInfo.java:140)
at android.app.Activity.onMenuItemSelected(Activity.java:2660)
at android.support.v4.app.FragmentActivity.onMenuItemSelected
(FragmentActivity.java:408)
at
android.support.v7.app.AppCompatActivity.onMenuItemSelected
(AppCompatActivity.java:195)
at android.support.v7.view.WindowCallbackWrapper.onMenuItemSelected
(WindowCallbackWrapper.java:113)
Don't set pass layout id directly like R.layout.contactstory instead use layout inflater to inflate layout like below code
View view = LayoutInflater.from(context).inflate(R.layout.contactstory,null);
dialog.setContentView(view);
TextView t1=(TextView)view.findViewById(R.id.textyman);
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.