<FrameLayout
android:id="#+id/content_frame"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:foreground="#drawable/window_foreground">
<android.support.design.widget.AppBarLayout
android:id="#+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:elevation="#dimen/appbar_resting"
android:theme="#style/AppTheme.NoActionBar">
<com.ujjwal.univhub.components.SearchView
android:id="#+id/search_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</android.support.design.widget.AppBarLayout>
<include layout="#layout/fragment_university_lsit"></include>
</FrameLayout>
app_bar_main.xml
Here searchView is customFrameLayout.I used this xml file in my main activity xml file.
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<include
layout="#layout/app_bar_main"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<include
layout="#layout/floating_action_group"/>
</FrameLayout>
This is activity_main.xml
public class SearchView extends FrameLayout implements AdapterView.OnItemClickListener{
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
SuggestionAdapter.ViewHolder viewHolder = (SuggestionAdapter.ViewHolder)view.getTag();
University selectedUniversity = viewHolder.getUniversity();
String sendData = FilterBuilder.createCodeFilter(selectedUniversity.getCode()).toJson();
//may occure error here
Log.d("serach by name", "onItemClick: error");
KeyListener listener = new KeyListener((BaseActivity)getContext());//error thrown here.
listener.onClicked(sendData, Properties.LOCALHOST + Properties.UNIVERSITY_CODE);
queryInput.setText(selectedUniversity.getName());
suggestionAdapter.clear();
suggestionAdapter.notifyDataSetChanged();
}
}
In my MainActivity
setContentView(R.layout.activity_main);
java.lang.ClassCastException: android.view.ContextThemeWrapper cannot be cast to com.ujjwal.univhub.BaseActivity
My can't i cast context to the activity hosting the view.
ClassCastException simply means the object isn't of the type (or can't safely behave as the type) you are trying to cast into.
This might help.
If you create the view programatically providing an explicit context (your BaseActivity) using constructor new SearchView(yourBaseActivityContext), this exception should not be thrown. However, from xml, I'm not sure if the LayoutInflater uses your activity context or a more generic context for constructing the view.
Edit: (in response to op's comment)
i need it to post the data in UI through the handler of baseActivity. KeyListener will have to get access to BaseActivity instance. If i
have to make keylistener independent of activity then its will require
a complete turn over of the implementation.
In your BaseActivity, make an interface:
public interface KeyClickListener { void clicked(SomeUiData data) }
add a method to your SearchView class to register a listener (e.g. your BaseActivity) which can be notified
private BaseActivity.KeyClickListener mListener;
public void setKeyClickListener(BaseActivity.KeyClickListener listener) {
mListener = listener;
}
in your BaseActivity's onCreate() (so that listener is registered before any user clicks can be performed):
mSearchView = findViewById... ;// get your SearchView instance
mSearchView.setKeyclickListener(new KeyClickListener() {
void clicked(SomeUiData newUiInfo) {
// update your UI using newUiInfo here
// use any handler accessible in your BaseActivity
}
});
So, effectively, now you have a listener which is declared in your BaseActivity which can perform any UI update actions without KeyListener needing BaseActivity instance. Pass mListener to your KeyListener and in your original KeyListener.onClicked() method, you could invoke any methods you declared in KeyClickListener with the click/ui data which can update the UI (because it is declared in the activity).
Related
I am using mvvm and data binding on my project.I have a recycler view and make an adapter.my data shown in recycler view fine.Now i want to create item click listener.
So at the end of my adapter i create an interface like this:
interface MyClickListener {
void onItemClick(MyEntity myEntity);
}
after that i added this listener to constructor of my adapter:
public class MyRecyclerAdapter extends ListAdapter<MyEntity, MyRecyclerAdapter.ViewHolder> {
public MyClickListener clickListener;
public MyRecyclerAdapter(MyClickListener clickListener) {
super(MyEntity.DIFF_CALLBACK);
this.clickListener = clickListener;
}
#Override
public void onBindViewHolder(#NonNull ViewHolder holder, int position) {
MyEntity item = getItem(position);
holder.bind(item,clickListener);
}
and i send my callback to bind view xml :
private void bind(MyEntity item, MyClickListener clickListener) {
binding.setEntity(item);
binding.setClickListener(clickListener);
binding.executePendingBindings();
}
In xml:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="entity"
type="com.test.database.myEntity" />
<variable
name="clickListener"
type="com.test.home.MyClickListener" />
</data>
<androidx.cardview.widget.CardView
android:id="#+id/cardview_id"
android:layout_width="190dp"
android:layout_height="280dp"
android:layout_margin="5dp"
android:clickable="true"
android:foreground="?android:attr/selectableItemBackground"
app:cardBackgroundColor="#android:color/white"
app:cardCornerRadius="10dp"
app:cardElevation="5dp"
app:cardUseCompatPadding="true"
android:onClick="#{() -> clickListener.onItemClick(entity)}">
and i initialize this callback in my adapter:
MyRecyclerAdapter adapter = new MyRecyclerAdapter(myEntity -> {
Toast.makeText(getContext(), myEntity.getID(), Toast.LENGTH_SHORT).show();
});
But when i run application i got this error:
error: cannot find symbol class MyClickListener
What is my mistake?
I have to make class instead of interface and make a new object? There is no way to use interface ?
It's an issue related to access modifier of your interface. Try to change it public
public interface MyClickListener {
void onItemClick(MyEntity myEntity);
}
I noticed the SwipeRefreshLayout works when my RecyclerView is filled with data but not when the RecyclerView is empty. Is there a way to override this behavior?
My app fetches some data from the server and populates the RecyclerView. So for example, if the user has forgotten to turn on their internet but started my app, I want them to be able to turn it on and swipe up to refresh instead of going back and starting the activity again.
Here's is my activity's xml. I have removed some code to make this less verbose. I had one more button outside of the SwipeRefreshLayout and I have also removed my constraints.
<?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"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".BreakfastActivity"
android:orientation="vertical">
<android.support.v4.widget.SwipeRefreshLayout
android:id="#+id/b_swipe_refresh_layout"
android:layout_width="match_parent"
android:layout_height="0dp">
<android.support.v7.widget.RecyclerView
android:id="#+id/rv_breakfast"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</android.support.v4.widget.SwipeRefreshLayout>
</android.support.constraint.ConstraintLayout>
And this is the .java file:
public class BreakfastActivity extends AppCompatActivity implements SwipeRefreshLayout.OnRefreshListener {
// to display the menu
RecyclerView rv_breakfast;
RecyclerView.Adapter my_adapter;
// the actual menu
ArrayList<Menu> menu;
private SwipeRefreshLayout swipeRefreshLayout;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_breakfast);
rv_breakfast = findViewById(R.id.rv_breakfast);
swipeRefreshLayout = findViewById(R.id.b_swipe_refresh_layout);
swipeRefreshLayout.setRefreshing(true);
swipeRefreshLayout.setOnRefreshListener(this);
rv_breakfast.setLayoutManager(new LinearLayoutManager(this));
new GetMenu(this).execute();
}
#Override
public void onRefresh() {
new GetMenu(this).execute();
}
static class GetMenu extends GetMenuBase {
WeakReference<BreakfastActivity> context;
GetMenu(BreakfastActivity b) {
context = new WeakReference<>(b);
meal_type = "breakfast";
b.swipeRefreshLayout.setRefreshing(true);
}
#Override
protected void onPostExecute(JSONObject parent) {
// process the output of the server side script script
b.swipeRefreshLayout.setRefreshing(false);
}
}
The java file is again devoid of some code not concerning the question. GetMenuBase extends an AsyncTask and implements doInBackground() and makes a call to the server and returns the JSON output.
The problem is that when your RecyclerView is empty then your height will be 0dp because you've set the height to wrap_content and 0dp to your SwipeRefreshLayout.
Change the height of your SwipeRefreshLayout or your RecyclerView to match_parent.
even though i googled extensively, i cant find a solution to my problem. I am new to programming, so please consider that in your answer.
I have a Main Activity and a Menu bar at the bottom. Since the menu bar is scrollable and i want to have it for all the different screens, i figured out that i could - instead of making an intent for a new activity - just put a fragment on top of the existing screen (and spare out the menu bar).
But i fail at programatically opening that fragment. I tried the following, but it doesnt even recognice the ID of the FrameLayout.
I was trying to replace a FrameLayout in my Main Activities' xml file with the fragment:
FragmentManager fragmentManager = getSupportFragmentManager();
android.support.v4.app.FragmentTransaction ft =
fragmentManager.beginTransaction();
ft.replace(R.id.idOfFrameLayout, new nameOfFragmentClass());
ft.commit();
EDIT:
It works after i implemented OnFragmentInteractionListener into the Main Activity. Thanks to everyone!
My original answer suggested using add instead of replace for the Fragment transaction. I have since learned that either can be used in this case. For more info on the differences, see this, this and this
Since my add/replace idea was off base, I've changed my answer to a simple Fragment tutorial. One Fragment inflated from one Activity. I've included the code for Fragment to Activity communication (the reason for the OPs problem) but left comments on what could be deleted if that's not needed. The only thing done in the Activity is launching the Fragment then receiving a message back from that Fragment. The Fragment has a button that will cause a message to be sent back to the Activity. The message will then be Logged.
In both classes, if no communications is needed from the Fragment to the Activity, delete anything that is marked TODO:
// this is what controls whether you use a Fragment from the support library
import android.support.v4.app.FragmentManager;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
public class SimpleActivity extends AppCompatActivity
// TODO: delete "implements SimpleFragment.OnFragmentInteractionListener" from this
// line (leave the '{')
implements SimpleFragment.OnFragmentInteractionListener {
private static final String TAG = SimpleActivity.class.getSimpleName();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_simple);
SimpleFragment fragment = new SimpleFragment();
FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction()
.add(R.id.fragment_container, fragment)
.commit();
}
// TODO: this entire method can be deleted
#Override
public void onFragmentInteraction(String message) {
// This is where you can act on the message from the Fragment. You would do
// things that are done from an Activity or you may pass the message on
// to another Fragment.
Log.d(TAG, message);
}
}
Most import statements aren't shown, I left this one in to indicate using the support library
import android.support.v4.app.Fragment;
public class SimpleFragment extends Fragment {
private OnFragmentInteractionListener mListener; // TODO: delete this line
public SimpleFragment() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View rootView = inflater.inflate(R.layout.fragment_simple, container, false);
Button button = (Button)rootView.findViewById(R.id.msg_button);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
// TODO: delete these 3 lines, do something else with the button click
if (mListener != null) {
mListener.onFragmentInteraction("Message from Fragment");
}
}
});
return rootView;
}
// TODO: this entire method can be deleted
#Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof OnFragmentInteractionListener) {
mListener = (OnFragmentInteractionListener) context;
} else {
throw new RuntimeException(context.toString()
+ " must implement OnFragmentInteractionListener");
}
}
// TODO: this entire method can be deleted
#Override
public void onDetach() {
super.onDetach();
mListener = null;
}
// TODO: this entire method can be deleted
public interface OnFragmentInteractionListener {
void onFragmentInteraction(String message);
}
}
Now the layout files. 1st activity_simple.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/fragment_container"
>
</FrameLayout>
</android.support.constraint.ConstraintLayout>
Then fragment_simple.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="Info to Activity"
android:id="#+id/msg_button"
/>
</RelativeLayout>
This is a very basic sample. Here is a more extensive guide for Fragment usage.
When you use the 'replace' method, you need 3 things:
1- the container id, which is the view that is going to hold your fragment's view.
2- the fragment instance you want to use and
3- the fragment instance's tag, but this is optional.
Given that, let's say you have the following layout for your activity:
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
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="match_parent">
<android.support.design.widget.AppBarLayout
...
</android.support.design.widget.AppBarLayout>
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior">
<FrameLayout
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</android.support.v4.widget.NestedScrollView>
<android.support.design.widget.BottomNavigationView
android:id="#+id/navigation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:background="?android:attr/windowBackground"
app:menu="#menu/navigation" />
</android.support.design.widget.CoordinatorLayout>
The FrameLayout which id is "container" is the holding view, so that's the id you've gotta use:
ft.replace(R.id.container, new nameOfFragmentClass());
I suggest use this library to manage fragments:
FragmentManagerAndroid
In my app I extend LinearLayout and RelativeLayout, so every time I need to declare a layout in XML (which you may know it's quite often) I need to write a long tag, in order to point to the view's package.
So the XML files look like this:
<com.company.material.widget.LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.company.material.widget.RelativeLayout
android:layout_width="match_parent"
android:layout_height="#dimen/titlebar_height"
android:background="#color/primary"
android:orientation="horizontal">
<TextView
style="#style/Text.Field.Small"
android:id="#+id/form_title"
android:layout_width="wrap_content"
android:layout_height="match_parent"/>
<Button
style="#style/Button.Main"
android:id="#+id/form_submit"
android:layout_width="wrap_content"
android:layout_height="match_parent"/>
</com.company.material.widget.RelativeLayout>
<com.company.essentials.view.FormView
android:id="#+id/formview"
android:layout_width="match_parent"
android:layout_height="match_parent">
...
This is chaotic!
Is there any way to abbreviate this? Like AppLinearLayout instead of com.company.material.widget.LinearLayout?
Thanks in advance!
You could move your View class to the android.view package. This would allow you to just write <AppRelativeLayout /> instead of <com.company.material.widget.AppRelativeLayout />, since View tag names without package prefixes are 'auto-completed' to the android.view package.
If you don't want to move your whole class to this package, you may just create a dummy sub-class à la:
package android.view;
public class AppRelativeLayout extends com.company.material.widget.RelativeLayout {
// same constructors as super class
public AppRelativeLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
}
You just have to make sure you don't use the same class names as the Android framework already does, such as RelativeLayout, since that would clash with the existing views and layout names. That's why I named the example above AppRelativeLayout.
If it's worth it to you, you can customize your activity's layout inflater.
public class MyActivity extends Activity implements LayoutInflater.Factory {
#Override public void onCreate(Bundle _icicle) {
super.onCreate(_icicle);
setContentView(R.layout.my_activity);
}
#Override public Object getSystemService(String _name) {
Object service = super.getSystemService(_name);
if(LAYOUT_INFLATER_SERVICE.equals(_name)) {
LayoutInflater myInflater = ((LayoutInflater)service).cloneInContext(this);
myInflater.setFactory(this);
return myInflater;
}
return service;
}
#Override public View onCreateView (String _tag, Context _ctx, AttributeSet _as) {
if("mytag".equals(_tag))
return new MyLinearLayout(_ctx, _as);
else
return null;
}
}
setContentView will call getSystemService to obtain a layout inflater, and for each tag that layout inflater will query each of its factories (including our custom one) to see if the factory knows how to create the object that corresponds to that tag.
There is alternative option as well if you like you can do it like this
<view
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
class="com.company.material.widget.LinearLayout"
android:orientation="vertical">
I have an Activity with a ViewPager containing multiple fragments. How can I now access a TextView in one of that fragments to change its text from the main activity? I tried multiple ways and they all ended with errors
this is My headerAdapter
public class HeaderAdapter extends FragmentPagerAdapter {
private List<Fragment> fragments;
public HeaderAdapter(FragmentManager fm,List<Fragment> fragments) {
super(fm);
this.fragments=fragments;
}
#Override
public Fragment getItem(int position) {
return this.fragments.get(position);
}
#Override
public int getCount() {
return this.fragments.size();
}
And my header.xml contains an imageview and a text view like this:
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/fj"
android:textColor="#ff0000"
android:textSize="100dp"
android:text="aaa"
/>
<ImageView
android:alpha="0.7"
android:layout_width="wrap_content"
android:layout_height="203dp"
android:id="#+id/head1"
android:scaleType="fitXY"
android:src="#drawable/dash_widget_0"/>
</RelativeLayout>
My view pager like this:
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent"
android:weightSum="1">
<android.support.v4.view.ViewPager
android:layout_width="wrap_content"
android:layout_height="203dp"
android:id="#+id/header_pager"
>
</android.support.v4.view.ViewPager>
in mainActivity I need to change the textView?
You shouldn't access the textview from activity. That would be very hard to understand later and also that would cause total mess in your code.
What you should do is to implement public methods that do the work inside of a fragment. Accessing this method can be done in a super easy way by calling (YOUR_FRAGMENT_TYPE)HeaderAdapter#getItem(index). To avoid exceptions in casting the types check the Fragment with instanceOf.
Even though their might be a better way to implement what you are doing. You can get a reference to your Fragment from a view pager using this method
private Fragment getFragmentFromPager(int index) {
return getSupportFragmentManager().findFragmentByTag("android:switcher:" + R.id.header_pager + ":" + index);
}
Just keep a public reference to your TextView inside the Fragment so you can access it.