Load fragment with layout from another APK - java

I have two APKs. First APK is a main APK which loads second APK.
MainActivity.java (first APK): Object mainFragment was loaded by DexClassLoader early
setContentView(R.layout.activity_main);
LinearLayout fragContainer = findViewById(R.id.main_fragment);
LinearLayout ll = new LinearLayout(context);
ll.setOrientation(LinearLayout.HORIZONTAL);
ll.setId(View.generateViewId());
getSupportFragmentManager().beginTransaction().add(ll.getId(), mainFragment, "mainFragment").commit();
fragContainer.addView(ll);
activity_main.xml (first APK):
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal" >
<LinearLayout
android:layout_height="match_parent"
android:layout_width="match_parent"
android:id="#+id/main_fragment"
android:orientation="vertical"
/>
</LinearLayout>
MainFragment.java (second APK):
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup viewGroup, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_main, viewGroup, false);
view.findViewById(R.id.emailSignInButton).setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
}
});
return view;
}
fragment_main.xml (second APK):
<?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">
<Button
android:text="#string/sign_in"
android:layout_width="88dp"
android:layout_height="wrap_content"
android:id="#+id/emailSignInButton"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" android:layout_marginStart="32dp"
android:layout_marginEnd="32dp" android:layout_marginTop="8dp"
tools:ignore="MissingConstraints" />
</androidx.constraintlayout.widget.ConstraintLayout>
I don't understand, why in the line
View view = inflater.inflate(R.layout.fragment_main, viewGroup, false);
view is always NULL. Do I need to load R.layout.fragment_main into memory like mainFragment?

#CommonsWare already answered you: you are loading classes, not resources.
In Android´s packages, you have both classes (Like R), and resources (Preprocessed XML files, like layouts, for example). A ClassLoader is capable of reading and loading the first ones, so, loading another´s APK R class is possible. But R´s indexes just point towards resource files, they don´t contain the actual values of said resources. So, the R class you sideloaded does not provide the route to a valid resource. If R could do that, then the APK/AAR/APKLib formats would not exists; regular JAR files would suffice. But they don`t,because android resource files are different from class files, and thus, a "link" of sorts is necessary to make them visible to your classes: the R class.
if you want to make your current code work, you need to replace fragment_main.xml with a coded viewgroup instance. Create a class/method providing a constraintLayout instance housing your button (MyViewFactory.createConstraintLayoutWithButton, for example), set the properties via code, and replace
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup viewGroup, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_main, viewGroup, false);
view.findViewById(R.id.emailSignInButton).setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
}
});
return view;
}
with something like
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup viewGroup, Bundle savedInstanceState) {
View view = MyViewFactory.createConstraintLayoutWithButton(getContext)
view.findViewById(R.id.emailSignInButton).setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
}
});
return view;
}
As long as you don´t use resources, you´ll be fine. That said, this approach is quite fragile, so maybe it would be better to create intent filters between your APKs in order to make APK 2 to work for APK 1.
Edit:
You can use the R constants as ids for your code generated views, as long as you set them manually first. For example:
public static ConstraintLayout createConstraintLayoutWithButton(Context context){
Constraintlayout mylayout = new ConstraintLayout(context);
//set up of the layout
Button myButton = new Button(context);
//set up of the button
button.setId(R.id.emailSignInButton);
myLayout.addView(button);
return mylayout;
}
And then you can locate that button with the id.

Related

Why is my app Crashing with RecyclerView?

I'm doing a recycler method in my android studio project but i have a problem.
Everytime i'm trying to findViewById my application is crashing.
Attempt to invoke virtual method 'android.view.View android.view.View.findViewById(int)' on a null object reference
And i don't understand why because i'm creating my view in the good way.
public View onCreateView(#NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
view = inflater.inflate(R.layout.fragment_carrito, container, false);
return view;
}
And i'm trying to continue with that
#Override
public void onAttach(#NonNull Context context) {
super.onAttach(context);
...
Log.e("test : ",Integer.toString(R.id.recyclerBuy));
recyView = view.findViewById(R.id.recyclerBuy);
/* recyView.setLayoutManager(new GridLayoutManager(getContext(),RecyclerView.VERTICAL));
recyView.setAdapter(buyAdapter);
*/
}
But i'm crashing at the line recyView = view.findViewById(R.id.recyclerBuy);. My R.id.recyclerBuy is not empty.
And here is my RecyclerView
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context=".ui.carrito.CarritoFragment">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recyclerBuy"
android:layout_width="match_parent"
android:layout_height="550dp"
android:layout_marginBottom="88dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent" />
...
</androidx.constraintlayout.widget.ConstraintLayout>
Anyone have a clue why i'm crashing everytime ?
This happens because onAttach() callback is called before onCreateView().
The easiest way to fix the issue is placing the code where you finding your RecyclerView in onCreateView(). Something like:
public View onCreateView(#NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
view = inflater.inflate(R.layout.fragment_carrito, container, false);
recyView = view.findViewById(R.id.recyclerBuy);
// here you recyView won't be null
return view;
}
A good article about fragments lifecycle: https://medium.com/androiddevelopers/the-android-lifecycle-cheat-sheet-part-iii-fragments-afc87d4f37fd

Include layout in view programmatically within a fragment

Alright so I have a fragment and I'd like to include, within its' xml file another layout which is programmatically inflated
Fragment:
public class zGoal_Fragment extends Fragment{
private LinearLayout todayView;
private View view;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
view = inflater.inflate(R.layout.main_goal_view, container, false);
todayView = (LinearLayout)view.findViewById(R.id.todayView);
return view;
}
}
xml's file for fragment:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="fill_parent"
android:id="#+id/todayView"
>
</LinearLayout>
and xml layout I want included within the above xml programmatically:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/goalEditLayout"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="100dp"
android:background="#color/test_color_two"
>
</LinearLayout>
I've tried a couple different methods all of which led to " on a null object reference" errors...
plz help :))
I suppose the solution which you are looking for are Android ViewStubs. These are dynamically inflated layouts.
For more information you could refer these :
How to use View Stub in android
https://developer.android.com/reference/android/view/ViewStub.html
However, if you don't wish to inflate one layout inside another during runtime, you could try this the include tag:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="fill_parent"
android:id="#+id/todayView">
<include layout="#layout/your_layout_file_name"/>
</LinearLayout>
Have you tried something like this:
LinearLayout child = getLayoutInflater().inflate(R.layout.goalEditLayout, null);
todayView.addView(child);
Edit:
Inside onCreateView:
LinearLayout inflatedLayout = (LinearLayout) inflater.inflate(R.layout.goalEditLayout, todayView, true);

NPE on fragment recreation. Button is null after orientation change in fragment

I have read all the other topics related to this issue and none seem to be helping me solve the problem.
I am doing some background task in an asynctask that needs to persist on orientation change (configChanges). I have gotten the data to persist I believe by using fragments and setRetainInstance(true), but my buttons (Im assuming any views really) are being set to null on recreation of the fragment on orientation change. This is causing a null pointer error when I try to operate on the buttons reference after orientation has change (fragment has been recreated).
I have tried wrapping the button and calls on the button in a statement to check if the bundle is null. This stops the crash but does not allow for the button to be used again on recreation of the fragment.
The following class extends Fragment.
#Override
public void onCreate(Bundle savedInstanceState) {
if (DEBUG) Log.d(TAG, "onCreate()");
super.onCreate(savedInstanceState);
setRetainInstance(true);
currentActivity = super.getActivity();
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
if (DEBUG) Log.d(TAG, "onActivityCreated(Bundle)");
super.onActivityCreated(savedInstanceState);
simpleForecastIntent = new Intent(currentActivity, SimpleForecastActivity.class);
Button fetchDataButton = (Button) currentActivity.findViewById(R.id.fetchDataButton);
if (savedInstanceState == null) {
fetchDataButton.setOnClickListener(fetchDataButtonListener);
fetchDataButton.setTransformationMethod(null);
}
locationManager = (LocationManager)
currentActivity.getSystemService(Context.LOCATION_SERVICE);
if (isTaskRunning) {
progressDialog = ProgressDialog.show(currentActivity, "Fetching data", "Please wait...");
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
if (DEBUG) Log.d(TAG, "onCreateView(LayoutInflater, ViewGroup, Bundle)");
return inflater.inflate(R.layout.main_menu, container, false);
}
main_menu.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:orientation="vertical"
tools:context=".activities.MainActivity">
<include layout="#layout/toolbar" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp">
<Button
android:id="#+id/fetchDataButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#+id/locationField"
android:text="#string/fetch_weather_data"
android:layout_centerHorizontal="true" />
<EditText
android:id="#+id/locationField"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:hint="#string/enter_location"
android:layout_alignEnd="#+id/fetchDataButton"
android:layout_alignLeft="#+id/fetchDataButton"
android:layout_alignRight="#+id/fetchDataButton"
android:layout_alignStart="#+id/fetchDataButton"
android:inputType="textPostalAddress" />
<ListView
android:id="#+id/previousLocationsList"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#+id/fetchDataButton"
android:layout_centerHorizontal="true" />
</RelativeLayout>
</LinearLayout>
I have spent far too many hours on something so trivial and really need a bump in the correct direction. How can I get the button to not be null on recreation of fragment?
EDIT SOLVED:
I was using the disposed of activity's context to find the view of the button. Instead of the new activities view context.
#Override
public void onCreate(Bundle savedInstanceState) {
if (DEBUG) Log.d(TAG, "onCreate()");
super.onCreate(savedInstanceState);
setRetainInstance(true);
currentActivity = super.getActivity();
}
The problem is that you are referencing your button in onActivityCreated instead of onCreateView. On orientation change, the view gets torn down and then recreated. You should override onCreateView and maintain the reference to your button from there.
UPDATE:
I see how your code is working, but in my opinion it's following a bad design pattern. You are still instantiating your view references inside of onActivityCreated, instead of immediately after they are inflated in onCreateView.
I would switch it to something like this:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
if (DEBUG) Log.d(TAG, "onCreateView(LayoutInflater, ViewGroup, Bundle)");
View root = inflater.inflate(R.layout.main_menu, container, false);
Button fetchDataButton = (Button) root.findViewById(R.id.fetchDataButton);
...
return root;
}

ListFragment List staying in the middle of the page

I'm having issues with my listFragment staying in the middle of the page. Like this:
http://i.imgur.com/60ZpelK.jpg
I'm using a listview in my xml document like this
<?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">
<ListView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#android:id/list"
android:layout_centerVertical="true"
android:layout_centerHorizontal="true"/>
Which i'm pretty sure has no issues. But my Java i'm guessing is a little more dubious as i'm still learning.
public class menu_4_fragment extends ListFragment implements AdapterView.OnItemClickListener {
View rootview;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// Basic way of implementing title, perhaps a better method should be used if found
getActivity().getActionBar().setTitle("Settings");
rootview = inflater.inflate(R.layout.menu4_layout, container, false);
return rootview;
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
// Brings the data from XML to view
ArrayAdapter adapter = ArrayAdapter.createFromResource(getActivity(), R.array.Settings_strings, android.R.layout.simple_list_item_1);
setListAdapter(adapter);
getListView().setOnItemClickListener(this);
}
}
Can you guys see anything that's potential causing this issue? Or perhaps what would be a better way to implement this list frag? I'm having a bit of an issue implementing this in the first place as i've already got a fragmentation from my navigation drawer and most tutorials don't show how to implement a listview beyond that.
Thanks guys.
Remove
android:layout_centerVertical="true"
Well, your XML layout clearly says that you want your ListView to reside in the middle. Try changing to android:layout_height="match_parent" if you want it to kind of start from the top of the screen.
Just remove these two lines
android:layout_centerVertical="true"
android:layout_centerHorizontal="true"

Adding a click listener to the Drag-Sort-Listview library

I've implemented this library based off a listfragment, and its implementation is very similar to this code sample on the library's github repo:
https://github.com/bauerca/drag-sort-listview/blob/master/demo/src/com/mobeta/android/demodslv/DSLVFragment.java
My question is, how do I implement a click listener?
This is my xml file:
<?xml version="1.0" encoding="utf-8"?>
<com.mobeta.android.dslv.DragSortListView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:dslv="http://schemas.android.com/apk/lib/com.mobeta.android.dslv"
android:id="#android:id/list"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="3dp"
dslv:drag_handle_id="#drawable/bg_handle"
android:layout_margin="3dp"
android:dividerHeight="2dp"
dslv:drag_enabled="true"
dslv:collapsed_height="2dp"
dslv:drag_scroll_start="0.33"
dslv:max_drag_scroll_speed="0.5"
dslv:float_alpha="0.6"
dslv:slide_shuffle_speed="0.3"
dslv:track_drag_sort="false"
dslv:float_background_color="#color/blue"
android:focusable="false"
android:focusableInTouchMode="false"
dslv:use_default_controller="false" />
And this is how I've tried to create a click listener, but it doesn't respond:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
mDslv = (DragSortListView) inflater.inflate(getLayout(), container, false);
mController = buildController(mDslv);
mDslv.setFloatViewManager(mController);
mDslv.setOnTouchListener(mController);
mDslv.setDragEnabled(dragEnabled);
SimpleFloatViewManager simpleFloatViewManager = new SimpleFloatViewManager(mDslv);
simpleFloatViewManager.setBackgroundColor(Color.TRANSPARENT);
mDslv.setFloatViewManager(simpleFloatViewManager);
mDslv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
System.out.println("Clicked!");
}
});
return mDslv;
}
It is because you use at the same time OnItemClickListener and OnTouchListener.
You have 3 options, just choose one you like:
1) Give up OnTouchListener
2) Return false from onTouch() when you need click to be generated
3) Generate click yourself mDslv.performClick() in onTouch()when required

Categories

Resources