I'm developing application that receive data from bluetooth device to fragment in viewpager.
my application's process it
MainActivity receive data from bluetooth device
MainActivity's static String value changed as a data from bluetooth device
fragment's progressbar in viewpager in MainActivity refresh data as a MainActivity's data(changed integer)
fragment's progressbar refresh data and view whenever MainActivity's data is Changed
public class BalanceFragment extends Fragment{
public static String data = "0";
private String old = data;
private ProgressBar progressBar;
private TextView textView;
private Handler handler;
private MyThread myThread;
public BalanceFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_balance, container, false);
}
#Override
public void onStop() {
try{
myThread.stop();
} catch (Exception e){
e.printStackTrace();
}
}
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
progressBar = (ProgressBar) view.findViewById(R.id.progressBar);
textView = (TextView) view.findViewById(R.id.balanceProgressText);
myThread = new MyThread();
myThread.run();
}
#Override
public void onResume() {
myThread.run();
}
class MyThread extends Thread{
#Override
public void run() {
while(true) {
try {
this.sleep(5000);
MainActivity.println("test", "test");
} catch (Exception e){
e.printStackTrace();
}
if (old != data){
old = data;
progressBar.setProgress(Integer.valueOf(data));
textView.setText(data);
progressBar.invalidate();
textView.invalidate();
}
}
}
}
}
however.... when I launch this application, view is frozen......
I don't know reason why my app is frozen
You can create a interface to communicate with fragment, for example:
public class TestActivity extends Activity {
interface DataCallback {
void onData(String data);
}
DataCallback callback;
#Override
protected void onResume() {
super.onResume(savedInstanceState);
SampleFragment fragment = SampleFragment.newInstance();
callback = fragment.getCallback();
getFragmentManager().beginTransaction().replace(R.id.container, fragment, "tag").commit()
}
public static class SampleFragment extends Fragment implements DataCallback {
public static SampleFragment newInstance() {
Bundle args = new Bundle();
SampleFragment fragment = new SampleFragment();
fragment.setArguments(args);
return fragment;
}
public DataCallback getCallback() {
return this;
}
#Override
public void onData(String data) {
//Update UI with data
}
}
}
every time your activity receives a new data you should call:
callback.onData(variableValue)
and pass the new data.
Related
I want to make my fragment wont re-load the data when configuration change with view model.
So, i try to make an App about gitHub User.
My Main Activity contain Search view to search user and show the result with recyclerview.
My Detail Activity is showing the detail of user when one of user in the list clicked, and in my detail activity i use Tab Layout and view pager to show user's followers and following.
My ViewModel for activity works well and can keep my data when orientation change.
But when i do the same to my fragment, my fragment keep re-loading new data when orientation change.
Here my fragment
i use View Model to request data
public class FollowingFragment extends Fragment {
public static final String KEY_FOLLOWING = "key_following";
private RecyclerView recyclerView;
private FollowingViewModel followingViewModel;
private FollowingAdapter followingAdapter;
ShimmerFrameLayout shimmerFrameLayout;
LinearLayoutManager layoutManager = new LinearLayoutManager(getContext(), LinearLayoutManager.VERTICAL, false);
ArrayList<FollowingResponse> followingResponse = new ArrayList<>();
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_following, container, false);
}
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
recyclerView = view.findViewById(R.id.rv_following);
shimmerFrameLayout = view.findViewById(R.id.shimmer_frame_layout2);
followingViewModel = ViewModelProviders.of(this).get(FollowingViewModel.class);
followingAdapter = new FollowingAdapter();
followingAdapter.setOnItemClickCallback(new FollowingAdapter.OnItemClickCallback() {
#Override
public void onItemClicked(FollowingResponse followingResponse) {
showSelectedItem(followingResponse);
}
});
//this is what i've tried and worked, but my fragment result with double or triple the data.
if (savedInstanceState == null){
}
else {
followingResponse = savedInstanceState.getParcelableArrayList(KEY_FOLLOWING);
}
showRecyclerView();
getData();
}
private void getData() {
followingViewModel.setDataFollowing(DetailUserActivity.clickedUser);
followingViewModel.getDataFollowing().observe(getViewLifecycleOwner(), new Observer<ArrayList<FollowingResponse>>() {
#Override
public void onChanged(ArrayList<FollowingResponse> followingResponses) {
shimmerFrameLayout.setVisibility(View.GONE);
shimmerFrameLayout.stopShimmer();
followingAdapter.setData(followingResponse);
followingResponse.addAll(followingResponses);
recyclerView.setAdapter(followingAdapter);
followingAdapter.notifyDataSetChanged();
}
});
}
private void showRecyclerView() {
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(layoutManager);
}
#Override
public void onResume() {
super.onResume();
followingViewModel.setDataFollowing(DetailUserActivity.clickedUser);
}
private void showSelectedItem(FollowingResponse item) {
Intent intent = new Intent(getContext(), DetailUserActivity.class);
intent.putExtra("EXTRA_DATA", item.getLogin());
startActivity(intent);
}
#Override
public void onAttach(#NonNull Context context) {
super.onAttach(context);
}
#Override
public void onSaveInstanceState(#NonNull Bundle outState) {
outState.putParcelableArrayList(KEY_FOLLOWING, followingResponse);
super.onSaveInstanceState(outState);
}
}
Detail Activity
public class DetailUserActivity extends AppCompatActivity {
private TextView tvName, tvNickname, tvLocation, tvCompany, tvEmail, tvWebsite;
private TextView tvCountFollowers, tvCountRepository, tvCountFollowing;
private ImageView imgProfile;
ProgressBar progressBar;
public static String clickedUser;
UserViewModel userViewModel;
private ViewPager viewPager;
TabLayout tabLayout;
private final String EXTRA_DATA = "EXTRA_DATA";
private static final String EXTRA_FOLLOW = "extra_follow";
// Fragment followingFragment;
// FragmentTransaction transaction;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.detail_user_activity);
// i've tried this, but when the orientation change, my detail activity force close and throw me to my main activity.
// if (savedInstanceState != null){
// followingFragment = getSupportFragmentManager().getFragment(savedInstanceState, KEY_FOLLOWING);
// } else {
// followingFragment = new FollowingFragment();
//
// getSupportFragmentManager().beginTransaction().replace(R.id.tv_view_pager, followingFragment).commit();
// }
tvName = findViewById(R.id.tv_item_name);
tvNickname = findViewById(R.id.tv_item_nickname);
tvLocation = findViewById(R.id.tv_item_location);
tvCompany = findViewById(R.id.tv_item_company);
tvEmail = findViewById(R.id.tv_item_email);
tvWebsite = findViewById(R.id.tv_item_website);
imgProfile = findViewById(R.id.img_item_photo);
tvCountFollowers = findViewById(R.id.tv_count_follower);
tvCountRepository = findViewById(R.id.tv_count_repo);
tvCountFollowing = findViewById(R.id.tv_count_following);
progressBar = findViewById(R.id.progressbar2);
setForUserData();
setForTabLayout();
}
private void setForUserData(){
Intent detailIntent = getIntent();
clickedUser = detailIntent.getStringExtra("EXTRA_DATA");
userViewModel = new ViewModelProvider(this, new ViewModelProvider.NewInstanceFactory()).get(UserViewModel.class);
userViewModel.setUserUVM(clickedUser);
userViewModel.getUserUVM().observe(this, new Observer<UserResponse>() {
#Override
public void onChanged(UserResponse userResponse) {
progressBar.setVisibility(View.GONE);
Glide.with(getApplicationContext())
.load(userResponse.getAvatarUrl())
.into(imgProfile);
tvName.setText(userResponse.getLogin());
tvLocation.setText(userResponse.getLocation());
tvNickname.setText(userResponse.getName());
tvCompany.setText(userResponse.getCompany());
tvEmail.setText(userResponse.getEmail());
tvWebsite.setText(userResponse.getBlog());
tvCountRepository.setText(String.valueOf(userResponse.getPublicRepos()));
tvCountFollowers.setText(String.valueOf(userResponse.getFollowers()));
tvCountFollowing.setText(String.valueOf(userResponse.getFollowing()));
}
});
}
private void setForTabLayout(){
tabLayout = findViewById(R.id.tv_tab_layout);
viewPager = findViewById(R.id.tv_view_pager);
viewPager.setAdapter(new ViewPagerAdapter(getSupportFragmentManager(), tabLayout.getTabCount()));
viewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(tabLayout));
tabLayout.setTabMode(TabLayout.MODE_FIXED);
tabLayout.setOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
#Override
public void onTabSelected(TabLayout.Tab tab) {
viewPager.setCurrentItem(tab.getPosition());
}
#Override
public void onTabUnselected(TabLayout.Tab tab) {
}
#Override
public void onTabReselected(TabLayout.Tab tab) {
}
});
}
public class ViewPagerAdapter extends FragmentStatePagerAdapter {
int totalTab;
#SuppressWarnings("deprecation")
ViewPagerAdapter(FragmentManager fragmentManager, int totalTabs) {
super(fragmentManager);
this.totalTab = totalTabs;
}
#SuppressWarnings("ConstantConditions")
#NonNull
#Override
public Fragment getItem(int position) {
switch (position){
case 0:
return new FollowersFragment();
case 1:
return new FollowingFragment();
default:
return null;
}
}
#Override
public int getCount() {
return totalTab;
}
}
// #Override
// protected void onSaveInstanceState(#NonNull Bundle outState) {
// getSupportFragmentManager().putFragment(outState, KEY_FOLLOWING, followingFragment );
// super.onSaveInstanceState(outState);
// }
}
im trying to solve my FollowingFragment first, then ill do the same on my FollowerFragment.
im really new in programming, and have no one to ask.. can someone help me to solve my problem with keep data in fragment when orientation change?
followingViewModel = ViewModelProviders.of(this).get(FollowingViewModel.class);
In Fragment insted of writing this code in onViewCreated() write it inside OnCreate()
onViewCreated() will get called everytime the orientation changes so it will create new instance of view model everytime the orientation changes making viewmodel to reload the data
If you need to load data only once for the FollowingViewModel a good place to do it should be inside the constructor of the view model in this way you won't need to worry about the orientation change of the fragment.
I have activity that contain fragment.
This fragment have a lottie animation with lottie_loop="false",
that means, once the animation finish first loop , the animation will be and.
I want to listen for this event(animation end) in activity that contain this fragment, but some this wrong with my code, and I have white screen.
I created interface for listen to even , and this is my code:
Fragment with lottie animation:
public class EntryFragmentAnimation extends Fragment {
private View view;
private LottieAnimationView mLavTwoHearts;
private boolean isAnimationEnd = false;
private OnAnimationEndListener iOnAnimationEndListener;
public interface OnAnimationEndListener {
void onAnimationEnd(boolean isAnimationEnd);
}
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
view = inflater.inflate(R.layout.fragment_entry_animation, container, false);
initView(view);
initAnimation();
return view;
}
#Override
public void onAttach(#NonNull Context context) {
super.onAttach(context);
try {
iOnAnimationEndListener = (OnAnimationEndListener) getActivity();
} catch (ClassCastException e) {
throw new ClassCastException(context.toString() + " must implement OnAnimationEndListener");
}
}
private void initView(View view) {
mLavTwoHearts = view.findViewById(R.id.lavTwoHearts);
}
private void initAnimation() {
mLavTwoHearts.playAnimation();
mLavTwoHearts.addAnimatorListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
isAnimationEnd = true;
iOnAnimationEndListener.onAnimationEnd(isAnimationEnd);
}
});
}
}
And an activity
public class LoginActivity extends AppCompatActivity implements EntryFragmentAnimation.OnAnimationEndListener {
private boolean isAnimationEnd = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
setEntryScreen();
listenToAnimationEnd();
}
private void setEntryScreen(){
getSupportFragmentManager()
.beginTransaction()
.add(R.id.container_login_fl, new EntryFragmentAnimation())
.commit();
}
private void listenToAnimationEnd(){
while (!isAnimationEnd){
Log.d(TAG, "listenToAnimationEnd: Animation playing");
}
Log.d(TAG, "listenToAnimationEnd: animation end");
}
#Override
public void onAnimationEnd(boolean isAnimationEnd) {
this.isAnimationEnd = isAnimationEnd;
}
}
While running the app , only white screen appear and in logcat running endless log with Animation playing
Instead of a listener I would suggest you to better use a ViewModel. You only need to create ViewModel class and create its instance in fragment but using the activity scope so that it will be available for all the fragment contained within the activity including activity itself.
In your fragment create a Shared ViewModel instance like below:
activity?.let {
sharedViewModel = ViewModelProviders.of(it).get(SharedViewModel::class.java)
}
Once the animation ends update the the ViewModel
sharedViewModel?.onAnimationFinished()
Note: Inside you ViewModel class, have any live data member which is being obeserved by your Activity and then just update the variable within the function.
In the activity we just need to create instance of our ViewModel and observe the required data like this
val sharedViewModel = ViewModelProviders.of(this).get(SharedViewModel::class.java)
sharedViewModel.animationEndEvent.observe(this, Observer {
it?.let {
// do some thing
}
})
StepFragmentSample.java:
public class StepFragmentSample extends Fragment implements Step {
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.step, container, false);
//initialize your UI
return v;
}
public VerificationError verifyStep() {
//return null if the user can go to the next step, create a new VerificationError instance otherwise
return null;
}
#Override
public void onSelected() {
//update UI when selected
}
#Override
public void onError(#NonNull VerificationError error) {
//handle error inside of the fragment, e.g. show error on EditText
}
}
MyStepAdapter.java:
public static class MyStepperAdapter extends AbstractFragmentStepAdapter {
public MyStepperAdapter(FragmentManager fm, Context context) {
super(fm, context);
}
#Override
public Step createStep(int position) {
final StepFragmentSample step = new StepFragmentSample();
Bundle b = new Bundle();
b.putInt(CURRENT_STEP_POSITION_KEY, position);
step.setArguments(b);
return step;
}
#Override
public int getCount() {
return 3;
}
#NonNull
#Override
public StepViewModel getViewModel(#IntRange(from = 0) int position) {
//Override this method to set Step title for the Tabs, not necessary for other stepper types
return new StepViewModel.Builder(context)
.setTitle(R.string.tab_title) //can be a CharSequence instead
.create();
}
}
MainActivity.java:
public class StepperActivity extends AppCompatActivity {
private StepperLayout mStepperLayout;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mStepperLayout = (StepperLayout) findViewById(R.id.stepperLayout);
mStepperLayout.setAdapter(new MyStepperAdapter(getSupportFragmentManager(), this));
}
I have copied the above code from this link.}
While debugging, the control does not goes to createStep Method In StepFragmentSample.java
while debugging from mainActivity.java, the control goes to MyStepAdapter.java in that first MyStepperAdapter() method then to getCount() method but not to createStep(). why? please help me. and It does not giving any exception. The app shows "unfortunately stopped".
I have a fragment that extends ListFragment and when I minimize the app and go back to it, the listview is empty.
Does it not restore automatically?
public class LocationListViewFrag extends ListFragment implements AdapterView.OnItemClickListener {
private static final String TAG = "LocationListViewFrag";
private ArrayList<LocationObj> mLocations = new ArrayList<>();
private LocationAdapter mTaxAdapter;
private LocationListViewListener mListener;
#Bind(R.id.listLabel)
TextView listLabel;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mTaxAdapter = new LocationAdapter(getActivity(), mLocations);
setListAdapter(mTaxAdapter);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_tax_location_list, container, false);
ButterKnife.bind(this, v);
return v;
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
getListView().setOnItemClickListener(this);
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
try {
mListener = (LocationListViewListener) context;
} catch (ClassCastException e) {
throw new ClassCastException(context.toString() + " must implement LocationListViewListener");
}
}
public void setLocations(ArrayList<LocationObj> Locations) {
mLocations = Locations;
mTaxAdapter.clear();
mTaxAdapter.addAll(mLocations);
mTaxAdapter.notifyDataSetChanged();
if(mLocations.isEmpty()){
listLabel.setVisibility(View.VISIBLE);
} else {
listLabel.setVisibility(View.GONE);
}
}
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
LocationObj aLocation = mLocations.get(position);
Log.d(TAG, String.format("Clicked - %s", aLocation.toString()));
mListener.onLocationSelected(aLocation);
}
public interface LocationListViewListener {
void onLocationSelected(LocationObj LocationObj);
}
}
Activity
onCreate
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.free_tax_prep_providers_new);
initDrawer();
ButterKnife.bind(this);
if (savedInstanceState != null) {
//Restore the fragment's instance
mLocationListViewFrag = (LocationListViewFrag)getSupportFragmentManager().getFragment(savedInstanceState, "mLocationListViewFrag");
} else if (findViewById(R.id.fragment_container) != null) {
// Check that the activity is using the layout version with
// the fragment_container FrameLayout
// Create a new Fragment to be placed in the activity layout
mLocationListViewFrag = new LocationListViewFrag();
// Add the fragment to the 'fragment_container' FrameLayout
getSupportFragmentManager().beginTransaction()
.add(R.id.fragment_container, mLocationListViewFrag).commit();
}
}
onSavedInstance
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
//Save the fragment's instance
getSupportFragmentManager().putFragment(outState, "mLocationListViewFrag", mLocationListViewFrag);
}
You can actually override onSaveInstanceState within the fragment itself instead of the activity. Doing that did the job for me when I was working with list fragments.
I have an ActionBar and it has Tabs.
In the MainActivity there is a String variable.
In one Tab there is a TextView and a method setTv(String string) what can change the text in it.
I would like to make a Listener interface what can call the setTv(String string) method, when the str String is changes in the MainActivity.
Could anybody help me and fill in this code with the implementation?
This is the MainActivity code:
public class MainActivity extends FragmentActivity implements ActionBar.TabListener, LocationListener{
ActionBar actionbar;
ViewPager viewpager;
FragmentPageAdapter ft;
String str;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
viewpager = (ViewPager) findViewById(R.id.pager);
ft = new FragmentPageAdapter(getSupportFragmentManager());
actionbar = getActionBar();
viewpager.setAdapter(ft);
actionbar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
actionbar.addTab(actionbar.newTab().setText("Run").setTabListener(this));
actionbar.addTab(actionbar.newTab().setText("Map").setTabListener(this));
actionbar.addTab(actionbar.newTab().setText("Statistics").setTabListener(this));
viewpager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
});
}
This is the Fragment code:
public class RunFragment extends Fragment {
TextView tv;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// TODO Auto-generated method stub
View view = inflater.inflate(R.layout.run_layout, container, false);
tv =(TextView)view.findViewById(R.id.textView1);
return view;
}
public void setTv(String string){
tv.setText(string);
}
}
Get your current fragment in your viewpager:
RunFragment fragment = (RunFragment) ft.getItem(viewPager.getCurrentItem());
Call your method where you want(after change str):
fragment.setTv("string");
For the best way i recommend to you make a class that extends Application
from the docs :
Base class for those who need to maintain global application state
So you can store your str variable in this class and notify any launched Activities of variable state change. Actually i recommend to use Observer pattern
MyApplication :
public class MyApplication extends Application {
private static MyApplication singleton;
private String str;
private ArrayList<StringObserver> observerList = new ArrayList<StringObserver>();
public MyApplication getInstance(){
return singleton;
}
#Override
public void onCreate() {
super.onCreate();
singleton = this;
}
public void setString(String str) {
this.str = str;
notifyObservers();
}
public void addObserver(StringObserver obs) {
observerList.add(obs);
}
public void removeObserver(StringObserver obs) {
observerList.remove(obs);
}
private void notifyObservers() {
for(StringObserver obs : observableList) {
obs.notifyAboutStringChanged(str);
}
}
}
StringObserver :
public interface StringObserver {
void notifyAboutStringChanged(String str);
}
And usage :
public class MainActivity extends FragmentActivity/*or Activity*/ implements StringObserver {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
MyApplication appState = ((MyApplication )this.getApplication());
appState.addObserver(this);
appState.setString("This string was changed");
}
#Override
public void notifyAboutStringChanged(String str) {
// do something
}
}
In other Activity/Fragment :
public class RunFragment extends Fragment {
TextView tv;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// TODO Auto-generated method stub
View view = inflater.inflate(R.layout.run_layout, container, false);
tv =(TextView)view.findViewById(R.id.textView1);
return view;
}
public void setTv(String string){
tv.setText(string);
(MyApplication) getActivity().getApplication().setString(string); // this string notify your activity about value change.
}
}