I've got a transition on the app I'm developing that fades all elements of a frame layout and swaps them for other elements. The animation works as intended, but from there I can't interact with the buttons on the new scene applied to the fragment. Here is the relevant parts of the code:
This is the frame layout where the animation happens
<FrameLayout
android:id="#+id/scene_root_index"
android:layout_width="match_parent"
android:layout_height="360dp"
app:layout_constraintBottom_toTopOf="#+id/indexSearchButton"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/imageView4">
<include layout="#layout/main_scene" />
</FrameLayout>
This is the main_scene:
<!--[...]-->
<ImageButton
android:id="#+id/newsMainIndexBtn"
android:layout_width="100dp"
android:layout_height="100dp"
android:backgroundTint="#00FFFFFF"
android:contentDescription="#string/descripcion_boton_noticias"
app:layout_constraintTop_toBottomOf="#+id/imageView6"
app:srcCompat="#drawable/ic_noticias"
tools:layout_editor_absoluteX="59dp" />
<!--[...]-->
This is the news scene, here is where I need the back button to work:
<!--[...]-->
<ImageButton
android:id="#+id/newsBack"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:backgroundTint="#00FFFFFF"
app:srcCompat="#drawable/ic_back" />
<!--[...]-->
This is the onCreateView method I'm using
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
setMainActivity((MainActivity) getActivity());
View view = inflateFragment(R.layout.fragment_index, inflater, container);
assert view != null;
sceneRoot = (ViewGroup) view.findViewById(R.id.scene_root_index);
mainScene = Scene.getSceneForLayout(sceneRoot, R.layout.main_scene, getActivity());
newsScene = Scene.getSceneForLayout(sceneRoot, R.layout.news_scene, getActivity());
transitionManagerForIndexScene = TransitionInflater.from(getActivity()).inflateTransitionManager(R.transition.transition_manager_for_index_scene, sceneRoot);
AppCompatImageButton news = view.findViewById(R.id.newsMainIndexBtn);
news.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
TransitionManager.go(newsScene);
}
});
The only solution that I've been able concieve is to make another layout with the same elements as the main one swapping out the frameView, but that animates all the screen and I feel like this is the correct way to do it, but I'm missing something.
I've tried to create the onclick event for the button everywhere in the code, but I couldn't find a place or time when the code recognized it, instead giving me a null pointer exception everytime.
Thanks for your time and for the help, I'm looking forward to be able to solve this issue.
Related
I ran into a problem with a RecyclerView and I thought you guys can help me.
So the problem is:
Inflating the item view xml takes too much time.
The layout is grid layout with 3 items in a row and each item has a complex UI look.
So first I've used android studio's profiler to analyse it and I saw that the CPU has a significant bump at the point of creation. Even if I remove the entire bind code, It still takes some time when I move into this screen/ scrolling inside the recycler view.
Going into separating the items to smaller items will be crazy because it's already a matrix (3 items in a row as I said).
So I thought about 2 things:
maybe I can create more view holders in advance, any idea how to do that? I saw methods like RecyclerView.setItemViewCacheSize and LayoutManager.setInitialPrefetchItemCount but no luck making it happens (it is still calling on create a lot of times)
What about AsyncLayoutInflater? does someone uses it in a RecyclerView?
Any new idea will be great!
This is how the view xml looks like:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
card_view:cardCornerRadius="3dp"
card_view:cardElevation="2.5dp"
card_view:cardUseCompatPadding="true">
<CustomView1>
</CustomView1>
<CustomView2>
</CustomView2>
<CustomView3>
</CustomView3>
<ImageView
android:id="#+id/imageView"
android:layout_width="wrap_content"
android:layout_height="30dp"
android:layout_gravity="end"
android:layout_marginEnd="2dp"
android:layout_marginStart="2dp"
android:layout_marginTop="2dp"
android:adjustViewBounds="true" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<include layout="#layout/someLayoutToInclude" />
</android.support.v7.widget.CardView>
</FrameLayout>
OnCreateViewHolder looks like:
LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
View view = layoutInflater.inflate(viewType, parent, false);
return new ItemViewHolder(view);
Grid Layout initialization:
final GridLayoutManager gridLayoutManager = new GridLayoutManager(getContext(), 3);
gridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
#Override
public int getSpanSize(int position) {
return 1;
}
});
Thanks
Here is the solution I did at the end:
As I mentioned, my issue was that onCreateViewHolder, creates a complex view with a huge hierarchy that cannot be changed (product-wise), and I had 3 items in a row(GridLayoutManager)
Because of that, I had 2 things that worked bad:
Scrolling fast/ flinging created new items while scrolling and the UI freezed for few milliseconds.
The animation between the previous screen and the screen with the RecyclerView
looked bad and leggy.
So what I did is:
As I mentioned above , there's an AsyncLayoutInflater.
In order to use it, I needed to inflate a dummy ViewGroup, and after that to add the async inflated view to this dummy view.
Of course some extra logic should be done for supporting onBindViewHolder.
public ItemViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
View view = layoutInflater.inflate(R.layout.async_holder_item, parent, false);
AsyncLayoutInflater asyncLayoutInflater = new AsyncLayoutInflater(parent.getContext());
asyncLayoutInflater.inflate(viewType, (ViewGroup) view, new AsyncLayoutInflater.OnInflateFinishedListener() {
#Override
public void onInflateFinished(#NonNull View view, int resid, #Nullable ViewGroup parent) {
parent.addView(view);
}
});
return new ItemViewHolder(view, viewType);
}
You can also use a shimmer or loading state at the dummy view (keep the layout simple!) and add android:animateLayoutChanges="true" to the top view group to make it smoother
I try to make a button active fragment, leading to a new layout with the url of this fragment. But it is not working. Something wrong. Please help
no receive error, but the button does not work.
code fragment
public class ContentFragment extends Fragment{
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
final View view = inflater.inflate(R.layout.content_fragment, container, false);
final View button = view.findViewById(R.id.bSendUrl);
button.setOnClickListener(
new View.OnClickListener() {
#Override
public void onClick(View v) {
EditText simpleEditText = (EditText) getView().findViewById(R.id.bTextUrl);
String strValue = simpleEditText.getText().toString();
Intent intent = new Intent(getActivity(), MainActivity.class);
intent.putExtra("url", strValue);
startActivity(intent);
}
}
);
return view;
}
}
xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.TextInputLayout
android:id="#+id/input_TEXT_Url"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:layout_below="#+id/include"
android:layout_alignParentEnd="true"
android:layout_alignParentStart="true"
android:layout_marginTop="6dp">
<EditText
android:id="#+id/bTextUrl"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textUri"
android:hint="#string/url_edit_text" />
</android.support.design.widget.TextInputLayout>
<Button
android:layout_width="#dimen/latimea_button_send_tv"
android:layout_height="#dimen/inaltimea_button_send_tv"
android:text="#string/button_send_android_tv"
android:id="#+id/bSendUrl"
style="?android:attr/borderlessButtonStyle"
android:layout_below="#+id/input_TEXT_Url"
android:paddingLeft="16dp"
android:layout_marginLeft="16dp" />
</RelativeLayout>
Edit
I am removed openjdk and installed clean javasdk and is work fine
You are trying to find a reference to a Button before the fragment's view hierarchy is inflated. The fragment transaction you're using does not immediately create the view hierarchy after commit() is called. This means the view with id bTextUrl is not yet created and findViewById() returns null.
Also, it's kind of strange for an activity to be messing with the views of child fragments. I recommend that you modify the views of the fragment within the fragment itself (not within the containing activity like you're doing now).
i wanted to create a terminal/console, where the user can enter commands. I know java, but i'm new to xml, so i wanted to know how i can spawn text under text and if it gets to long it should be scrollable, here's a picture:
and here's my xml cpde for it:
<?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:background="#d1d1d1">
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:weightSum="1">
<EditText
android:layout_width="175dp"
android:layout_height="wrap_content"
android:id="#+id/consoleText"
android:layout_gravity="bottom"
android:hint="Command"
android:layout_weight="0.99" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Send"
android:id="#+id/sendButton"
android:layout_gravity="bottom" />
</LinearLayout>
</LinearLayout>
and here's my code for getting the text:
public class FragmentConsole extends Fragment{
EditText text;
Button button;
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.console,
container, false);
button = (Button) view.findViewById(R.id.sendButton);
text = (EditText)view.findViewById(R.id.consoleText);
button.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View v)
{
Log.v("EditText", text.getText().toString());
}
});
return view;
}
}
So i will use some photoshopped pictures to show what i want to do, so each time i enter a command in this case i will use the example "command one","command two" & "command three", so and if i click the send button after each of them they should apper like this:
so and if the text reaches this black bar:
the above added text should get pushed in a scrollview and every new text will also get part of the scrollview, so that you can scroll through your commands later on.
I know this is a long post and i hope it is clear what i want to do and someone will know how i could do this. So thanks in advance :)
For this task you should consider using ListView and add every command as new row in this view. It'll also take care for scrolling and you'r text wont colide with your EditText.
What I have is a canvas that takes up nearly all of the screen, then under that I want a row of buttons and some other widgets. So I did this.
XML Code
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:id="#+id/myLayout"
android:orientation="vertical"
android:layout_height="match_parent" >
<com.zone.manager.Tab3
android:id="#+id/tab3_display"
android:layout_width="fill_parent"
android:layout_height="620dp" />
<LinearLayout
android:layout_width="match_parent"
android:orientation="horizontal"
android:layout_height="match_parent" >
<Button
android:id="#+id/addZone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Add Zone" />
<Button
android:id="#+id/helpZone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Help" />
<SeekBar
android:id="#+id/seekBar1"
android:paddingTop="9dp"
android:layout_width="179dp"
android:layout_height="wrap_content" />
</LinearLayout>
Java Code
public class Tab3 extends View implements OnTouchListener, OnClickListener {
public Tab3(Context context, AttributeSet attrs) {
View parent = (View) getParent();
addZone = (Button) parent.findViewById(R.id.addZone);
addZone.setOnClickListener(this);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
. . . draws a bunch of stuff
}
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.addZone:
String w = "W~20~0~0~0~0~0";
Log.d("ZoneSize", "Zone set");
MyApplication.preferences.edit().putString( "ZoneSize", w ).commit();
MyApplication.preferences.edit().putBoolean("ZoneSizeReady", true).commit();
break;
}
}
However my problem with this is that I believe the code is not reconising where the addZone is. Because when I have the addZone.setOnClickListener active my program will crash, but when I don't have it active the layout looks like how I want it. What must I do to fix this?
addZone = (Button) findViewById(R.id.addZone);
addZone will be null because com.zone.manager.Tab3 does not have any children
So it is obvious that your code will crash
So either you will give com.zone.manager.Tab children which require also to change the base class from View to ViewGroup,
or you start with com.zone.manager.Tab parent. Something like
View parent = (View) getParent ();
addZone = (Button) parent.findViewById(R.id.addZone);
i have some tips which will make you avoid such weird bugs and others:
the code of the custom view relies on an xml layout that uses the custom view .
this is bad coding.
instead, you should use LayoutInflater , use a layout xml file for the custom view for it , and then do the "findViewById" and add the clickListeners to any view inside that you wish.
i think it's also wrong to set the custom view to hold the click listener of the other views without checking which of them was clicked . either add a check , or add a different listener for each of them (which i personally prefer) .
I am trying to make my app more tablet-friendly, and so I'm trying to learn fragments. I want the typical two-pane layout, where the left side is the "navigation", and you click on one of the elements, and it changes the fragment on the right.
I can duplicate the tutorials that use a ListFragment for the left, and if you click on one of them, it updates the "details" fragment on the right.
I've tried the best I can to duplicate that code, and just use a LinearLayout, with buttons for the left side, so that if a button is clicked, it loads the appropriate fragment on the right, but it's not working.
When I commit the FragmentTransaction, I get java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first. What is the child's parent, and why do I have to call removeView on it?
I'm trying to load the fragment into a FrameLayout, but I've also just tried replacing another Fragment in the layout, and still get the error.
Ideally I want the left fragment to take up the whole screen until a button is pressed that requires a fragment to come in from the left, but one problem at a time I suppose.
Code
Main Activity
public class FragmentExample2Activity extends Activity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
}
SelectorFragment (Left side)
public class SelectorFragment extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
View fragment = inflater.inflate(R.layout.selector, container);
Button button1 = (Button) fragment.findViewById(R.id.button1);
button1.setOnClickListener(new OnClickListener(){
#Override
public void onClick(View v) {
getFragmentManager().beginTransaction().replace(R.id.detail_holder, new DetailsFragment(), "stuff").commit();
}
});
return fragment;
}
}
DetailsFragment
public class DetailsFragment extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
return inflater.inflate(R.layout.details, container);
}
}
main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<fragment class="com.coreno.testfragment.SelectorFragment"
android:id="#+id/select"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
/>
<fragment class="com.coreno.testfragment.DetailsFragment"
android:id="#+id/detail_holder"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="2"
/>
<!--
<FrameLayout
android:id="#+id/detail_holder"
android:layout_width="0px"
android:layout_height="match_parent"
android:layout_weight="2"
/>
-->
</LinearLayout>
When you inflate a layout xml file, don't specify the parent view. It sounds very counter-intuitive to not specify where you're putting this inflated view but it works.
So, for example, in your SelectorFragment change the line:
View fragment = inflater.inflate(R.layout.selector, container);
to
View fragment = inflater.inflate(R.layout.selector, null);
or even better
View fragment = inflater.inflate(R.layout.selector, container, false);
Don't forget to do the same in your DetailsFragment too.
When you inflate a layout inside the getView method of a Fragment, you must use the next inflate method:
inflater.inflate(R.layout.details, container, false);
The key is the third parameter. It must be false not to attach the inflated layout to the container, because the system already done it. If it is true or is not indicated a redundant group view is created.