I am trying to add a ViewPager to my Android app as a one-time setup screen for the user. But the problem I am facing is that the app keeps crashing when the orientation of the screen is changed amidst the setup process.
If the app is opened in either portrait or landscape mode and used without any orientation change, it works fine. But if the orientation is changed during runtime, using the method setCurrentItem(int position) on the ViewPager crashes the app.
Here's my Fragment class -
package com.cosine.arc;
import android.content.Context;
import android.graphics.Typeface;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.view.ViewPager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
/**
* A simple {#link Fragment} subclass.
*/
public class WelcomeFragment extends Fragment {
private int mPosition;
private Context mContext;
private ViewPager mPager;
private final int[] welcomeFragments = {R.layout.fragment_welcome1};
public WelcomeFragment() {
// Required empty public constructor
}
public WelcomeFragment(Context context, ViewPager viewPager, int position) {
this.mPosition = position;
this.mContext = context;
this.mPager = viewPager;
}
#Override
public View onCreateView(final LayoutInflater inflater, final ViewGroup container, Bundle savedInstanceState) {
View view = null;
try {
switch (mPosition) {
case 0:
view = inflater.inflate(R.layout.fragment_welcome1, container, false);
Typeface robotoLight = Typeface.createFromAsset(getActivity().getAssets(), "fonts/Roboto-Light.ttf");
TextView welcomeTxt1x2 = (TextView) view.findViewById(R.id.welcome_text_1_2);
Button startButton = (Button) view.findViewById(R.id.welcome_btn_1_1);
startButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
mPager.setCurrentItem(1);
}
});
welcomeTxt1x2.setTypeface(robotoLight);
}
} catch (NullPointerException e) {
e.printStackTrace();
}
return view;
}
}
And here's my FragmentActivity class with the FragmentStatePagerAdapter class within it -
package com.cosine.arc;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentStatePagerAdapter;
import android.support.v4.view.PagerAdapter;
public class IntroActivity extends FragmentActivity {
private static int NUM_PAGES = 3;
private NonSwipeableViewPager mPager;
private PagerAdapter mPagerAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_intro);
mPager = (NonSwipeableViewPager) findViewById(R.id.intro_pager);
mPagerAdapter = new IntroSliderAdapter(getSupportFragmentManager());
mPager.setAdapter(mPagerAdapter);
}
#Override
public void onBackPressed() {
if (mPager.getCurrentItem()==0) {
super.onBackPressed();
}
else {
mPager.setCurrentItem(mPager.getCurrentItem()-1);
}
}
public int getCurrentItem() {
return mPager.getCurrentItem();
}
public NonSwipeableViewPager getPagerUpdate() {
return mPager;
}
private class IntroSliderAdapter extends FragmentStatePagerAdapter {
public IntroSliderAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
return new WelcomeFragment(getBaseContext(), mPager, position);
}
#Override
public int getCount() {
return NUM_PAGES;
}
}
}
And here is the error log -
04-07 15:25:13.774 12186-12186/com.cosine.arc E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.cosine.arc, PID: 12186 java.lang.NullPointerException: Attempt to invoke virtual method 'void android.support.v4.view.ViewPager.setCurrentItem(int)' on a null object reference
at com.cosine.arc.WelcomeFragment$1.onClick(WelcomeFragment.java:58)
at android.view.View.performClick(View.java:5612)
at android.view.View$PerformClick.run(View.java:22288)
at android.os.Handler.handleCallback(Handler.java:751)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6123)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:867)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:757)
fragment_welcome1.xml -
<RelativeLayout 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"
tools:context="com.cosine.arc.WelcomeFragment">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/colorPrimary">
<TextView
android:id="#+id/welcome_text_1_1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="#string/welcome_to_"
android:textColor="#android:color/white"
android:textSize="36sp"
android:padding="16dp"
android:layout_marginTop="16dp"
android:gravity="center"/>
<ImageView
android:id="#+id/welcome_img_1_1"
android:layout_width="150dp"
android:layout_height="150dp"
android:src="#drawable/ic_logo"
android:layout_below="#id/welcome_text_1_1"
android:layout_centerHorizontal="true"/>
<TextView
android:id="#+id/welcome_text_1_2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#id/welcome_img_1_1"
android:text="#string/lets_get_things_started_"
android:textSize="42sp"
android:textColor="#android:color/white"
android:padding="16dp"
android:layout_marginTop="32dp"/>
<Button
android:id="#+id/welcome_btn_1_1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:background="#android:color/white"
android:text="Start"
android:textColor="#android:color/black"
android:layout_margin="16dp"
android:layout_alignParentRight="true"
android:drawableRight="#mipmap/ic_arrow_right_light"/>
</RelativeLayout>
Please do note that I have two different layout files fragment_welcome1.xml and fragment_welcome1.xml-land.
On orientation change, you will have to save the instance of the fragment if you want to retain the references. Have a look at this answer, hope it helps you. In a nutshell, i think you will need to save the instance of the fragment in your container activity's onSaveInstanceState(), and then recreate your saved fragment when orientation changes.
Please note that making config changes in the manifest is not the recommended way to save the instance of the fragment. Making config changes will lead to memory leaks.
Related
I have researched a lot on SharedElement Transitions , however I could only find Transitions for onclick event.
I want the animation between Fragments where the speed of animation is controlled by the user's scrolling speed. If the user stops in between while scrolling the objects should be still.
Example :
In this image , the viewpager is in the middle of screen1 and screen2 and the animating objects are in stopped state.
These objects move from one screen to another and also change their positions.
The translation animations part I have figured out but how to achieve this shared element transition with viewpager between fragments. And moreover it should scroll/animate with the speed of finger swipe.
MainActivity.java
package com.karan.onboardanimation;
import android.os.Bundle;
import android.support.v4.app.FragmentManager;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private MyFragmentPagerAdapter pagerAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.d(TAG, "onCreate: ");
ViewPager pager = (ViewPager) findViewById(R.id.pager);
FragmentManager fm = getSupportFragmentManager();
pagerAdapter = new MyFragmentPagerAdapter(fm);
pager.setAdapter(pagerAdapter);
pager.setCurrentItem(0);
}
}
FirstFragment.java- This fragment has an imageview which has to shared with second fragment when user is swiping on the viewpager.
package com.karan.onboardanimation;
import android.app.ActivityOptions;
import android.os.Build;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
public class FirstFragment extends Fragment {
private static final String TAG = "FirstFragment";
ImageView imageView;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (Build.VERSION.SDK_INT >= 21){
}
Log.d(TAG, "onCreate: ");
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
Log.d(TAG, "onCreateView: ");
View view = inflater.inflate(R.layout.first_fragment, container, false);
imageView = (ImageView) view.findViewById(R.id.img1);
ViewPagerTransformer viewPagerTransformer = new ViewPagerTransformer();
viewPagerTransformer.transformPage(imageView, 1);
return view;
}
}
MyFragmentPagerAdapter.java
package com.karan.onboardanimation;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.util.Log;
class MyFragmentPagerAdapter extends FragmentPagerAdapter {
private static final String TAG = "MyFragmentPagerAdapter";
final int PAGE_COUNT = 2;
public MyFragmentPagerAdapter(FragmentManager fm) {
super(fm);
Log.d(TAG, "MyFragmentPagerAdapter: ");
}
#Override
public Fragment getItem(int position) {
Log.d(TAG, "getItem: ");
switch (position) {
case 0:
return new FirstFragment();
case 1:
return new SecondFragment();
default:
return null;
}
}
#Override
public int getCount() {
return PAGE_COUNT;
}
}
second_fragment.xml
<?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:background="#d02030"
android:orientation="vertical">
<ImageView
android:id="#+id/img2"
android:layout_width="100dp"
android:layout_centerInParent="true"
android:src="#drawable/orange_centre"
android:transitionName="selectedIcon"
android:layout_height="100dp" />
</RelativeLayout>
first_fragment.xml
<?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="vertical">
<ImageView
android:id="#+id/img1"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_gravity="center"
android:src="#drawable/orange_centre" />
</LinearLayout>
Edit1-
This question is not a duplicate of link mentioned in comment because that question is a sub part of my question. The difference is that the animation in that question happens on a click with a static speed. However in my question, the speed of animation must be equal to the speed of ViewPager swipe speed.
You can try PageTransformer. It is not shared transition but more of view animation that is dependent on page position offset generated by swipe events.
I'm trying to learn android and am having an issue with this project. I have set up a fragment tab host with four fragments. What I would like to do is have the first three fragments be interactive with the user and when they tab over to the 4th tab, it will display all the information they put in on the first three. I figured I can pass the info overriding on onPause() as the trigger as I don't want to use a button press. Right now, I'm just trying to get the EditText to work to make sure I'm doing everything right. I'm not sure if I'm using the fragment transaction correctly, or the way I'm trying to collect the edit text field. Later on I hope to pass the information via a bundle. Any help would be appreciated.
Main:
package valdes.fragmenttabsmenu;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentTabHost;
import android.app.FragmentTransaction;
public class MainActivity extends FragmentActivity implements WelcomeFragment.WelcomeListener {
private FragmentTabHost mTabHost;
private String firstName;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTabHost = (FragmentTabHost) findViewById(android.R.id.tabhost);
mTabHost.setup(this, getSupportFragmentManager(), android.R.id.tabcontent);
mTabHost.addTab(mTabHost.newTabSpec("Home").setIndicator("Home", null), WelcomeFragment.class, null);
mTabHost.addTab(mTabHost.newTabSpec("Demographics").setIndicator("Demographics", null), DemographicsFragment.class, null);
mTabHost.addTab(mTabHost.newTabSpec("Questions").setIndicator("Questions", null), QuestionsFragment.class, null);
mTabHost.addTab(mTabHost.newTabSpec("Overview").setIndicator("Overview", null), OverviewFragment.class, null);
}
#Override
public void getFristName(String first_Name){
firstName = first_Name;
OverviewFragment fragment = new OverviewFragment();
FragmentTransaction ft = getFragmentManager().beginTransaction();
fragment.setFirstName(firstName);
ft.addToBackStack(null);
ft.commit();
}
}
Fragment 1 - getting info
package valdes.fragmenttabsmenu;
import android.content.Context;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.text.TextWatcher;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.TextView;
public class WelcomeFragment extends Fragment {
public WelcomeFragment() {
// Required empty public constructor
}
interface WelcomeListener{
public void getFristName(String firstName);
}
private WelcomeListener listener;
private String firstName;
#Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_welcome, container, false);
EditText editText = (EditText) view.findViewById(R.id.first_name);
firstName = editText.getText().toString();
return view;
}
#Override
public void onAttach(Context context){
super.onAttach(context);
this.listener = (WelcomeListener)context;
}
#Override
public void onPause(){
super.onPause();
if(listener != null){
listener.getFristName(firstName);
}
}
}
Fragment 1 XML
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="#dimen/activity_horizontal_margin">
<TextView
android:id="#+id/welcome"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_gravity="center_horizontal"
android:textSize="25sp"
android:text="#string/welcome_message"/>
<EditText
android:id="#+id/first_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="#string/first_name"/>
<EditText
android:id="#+id/last_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="#string/last_name"/>
<Spinner
android:id="#+id/birthday"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:entries="#array/months"/>
</LinearLayout>
Fragment 2 - getting info
package valdes.fragmenttabsmenu;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
public class OverviewFragment extends Fragment {
private String firstName;
private String lastName;
public OverviewFragment() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_overview, container, false);
}
#Override
public void onStart(){
super.onStart();
View view = getView();
if(view != null){
TextView name = (TextView) view.findViewById(R.id.OV_name);
name.setText(firstName);
}
}
public void setFirstName(String firstName){this.firstName = firstName;}
}
Fragment 2 XML
<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="valdes.fragmenttabsmenu.OverviewFragment">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_gravity="center_horizontal"
android:textSize="25sp"
android:layout_weight="1"
android:text="#string/overview"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_gravity="center_horizontal"
android:textSize="40sp"
android:text="Responses go here"/>
<TextView
android:id="#+id/OV_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="20sp"
android:text="TEST"/>
<Button
android:id="#+id/submit_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="#string/submit"/>
</LinearLayout>
Create a Listner for get/set data between fragments and implement it on activity like this
InteractionLister.java
public interface InteractionLister {
void setData(String data);
String getData();
}
MainActivity.java
public class TabActivity extends Activity implements InteractionLister{
private String mData;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public void setData(String data) {
this.mData = data;
}
#Override
public String getData() {
return mData;
}
}
TabFragment1.java
public class TabFragment1 extends Fragment {
InteractionLister interactionLister;
private static final String TAG = TabFragment1.class.getSimpleName();
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
Log.w(TAG,"Data from other fragment " + interactionLister.getData());
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
interactionLister = (InteractionLister) activity;
}
}
I had a gridview activity where I was populating the gridview with a custom object (a picture with some custom methods and an onclick listener). I could change the gridview size from the preferences menu. Everything worked beautifully.
I have since added some complexity to the code. Instead of using the normal Activity class, I am now using the FragmentActivity class, a fragmentPageAdapter, and a fragment to which my gridview is bound. This is because eventually I want the app to allow the user "swipe" from fragment to fragment.
Since using the fragment in this way, I am noticing a repeatable bug whenever I try to resize the gridview from the preferences menu: some of the objects get resized properly, other elements do not resize. Instead, they keep the same size that they had prior to changing the preference. Here is a screenshot:
This only happens when I try resizing from the preferences menu. Other calls to my resize method, such as on a configuration change, resize all of the gridview elements correctly.
I would appreciate any suggestions!
Here is my Code:
MAIN ACTIVITY:
package com.KhalidSorensen.animalsounds;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.content.res.Configuration;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.view.ViewPager;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.widget.GridView;
public class MainActivity extends FragmentActivity implements OnSharedPreferenceChangeListener{
private MyFragment m_myFragment = new MyFragment();
private ViewPager m_viewPager;
private static SharedPreferences m_prefs;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
m_prefs = PreferenceManager.getDefaultSharedPreferences(this);
m_prefs.registerOnSharedPreferenceChangeListener(this);
setContentView(R.layout.activity_main);
m_viewPager = (ViewPager) findViewById(R.id.pager);
m_viewPager.setAdapter(new MyAdapter(getSupportFragmentManager(), m_myFragment));
}
#Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
Log.i("Khalid","MainActivity: onSharedPreferenceChanged");
if (key.equals("key_prefs_enable_lscape")){
//do nothing for now
}else if (key.equals("key_prefs_picture_size")){
SetColumnWidth(m_myFragment.getM_gridView());
}
}
#Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
Log.i("Khalid","FragAnimalSounds: onConfigurationChanged");
SetColumnWidth(m_myFragment.getM_gridView());
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
Log.i("Khalid","MainActivity: onCreateOptionsMenu");
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.options_menu, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.lbl_opt_menu_settings:
Intent i = new Intent("android.intent.action.PREFERENCES");
startActivity(i);
break;
case R.id.lbl_opt_menu_quit:
finish();
break;
default:
finish();
break;
}
return true;
}
public static void SetColumnWidth(GridView GV) {
Log.i("Khalid","MainActivity: SetColumnWidth");
int NumColumns, DesiredColumnWidth;
//Get the desired number of columns from the key prefs
NumColumns = Integer.parseInt(m_prefs.getString("key_prefs_picture_size","2"));
//Determine the desired column width
if (NumColumns == 1){
DesiredColumnWidth = 200;
}else if (NumColumns == 2){
DesiredColumnWidth = 100;
}else{
DesiredColumnWidth = 50;
}
//Set the desired column width
GV.setColumnWidth(DesiredColumnWidth);
}
}
class MyAdapter extends FragmentPagerAdapter{
Fragment m_frag_A = null;
public MyAdapter(FragmentManager fm, Fragment A) {
super(fm);
m_frag_A = A;
}
#Override
public Fragment getItem(int i) {
return m_frag_A; //only 1 item for now
}
#Override
public int getCount() {
return 1;
}
#Override
public CharSequence getPageTitle(int i) {
return "Title Frag A"; //only 1 item for now
}
}
MY FRAGMENT (THERE IS ONLY ONE FOR NOW):
package com.KhalidSorensen.animalsounds;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.GridView;
public class MyFragment extends Fragment {
private GridView m_gridView;
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
Log.i("Khalid","FragAnimalSounds: onCreateView");
View view = inflater.inflate(R.layout.activity_animal_sounds, container, false);
m_gridView = (GridView) view.findViewById(R.id.lbl_gridView);
m_gridView.setAdapter(new VivzAdapter(view.getContext()));
MainActivity.SetColumnWidth(m_gridView);
return view;
}
public GridView getM_gridView() {
return m_gridView;
}
}
MY GRIDVIEW ADAPTER:
package com.KhalidSorensen.animalsounds;
import java.util.ArrayList;
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
public class VivzAdapter extends BaseAdapter {
private Context m_context;
private ArrayList<AnimalKind> m_list = new ArrayList<AnimalKind>();
VivzAdapter(Context ctx) {
m_context = ctx;
int[] animalphotos = { R.drawable.cat_1, R.drawable.cow_1,
R.drawable.dog_1, R.drawable.donkey_1, R.drawable.duck_1,
R.drawable.peacock_1, R.drawable.rooster_1, R.drawable.seal_1 };
for (int i = 0; i <= 7; i++) {
AnimalKind animalKind = new AnimalKind(m_context, animalphotos[i]);
m_list.add(animalKind);
}
}
#Override
public int getCount() {
return m_list.size();
}
#Override
public AnimalKind getItem(int position) {
return m_list.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int i, View v, ViewGroup vg) {
return m_list.get(i);
}
}
MY CUSTOM OBJECT CLASS. THESE OBJECTS ARE POPULATING THE GRIDVIEW:
package com.KhalidSorensen.animalsounds;
import android.content.Context;
import android.graphics.Color;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ImageView;
public class AnimalKind extends ImageView implements OnClickListener{
private int m_imageId;
public AnimalKind(Context ctx, int imageId) {
super(ctx);
m_imageId = imageId;
super.setImageResource(imageId);
super.setAdjustViewBounds(true);
super.setScaleType(ImageView.ScaleType.FIT_XY);
super.setPadding(1, 1, 1, 1);
super.setBackgroundColor(Color.BLACK);
super.setOnClickListener(this);
}
//#Override
public void onClick(View v) {
//Do Something
}
#Override
public void setPressed(boolean pressed) {
//Do Something
}
public int getM_imageId() {
return m_imageId;
}
}
MY PREFERENCE ACTIVITY:
package com.KhalidSorensen.animalsounds;
import android.os.Bundle;
import android.preference.PreferenceActivity;
import android.util.Log;
public class Preferences extends PreferenceActivity {
#SuppressWarnings("deprecation")
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences);
Log.i("Khalid", "Preferences: onCreate");
}
}
MY XML FOR MY MAIN ACTIVITY:
<android.support.v4.view.ViewPager xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<android.support.v4.view.PagerTitleStrip
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/lbl_title"
android:background="#33B5E5"
android:layout_gravity="top"
android:paddingTop="0dp"
android:paddingBottom="0dp"> "
</android.support.v4.view.PagerTitleStrip>
</android.support.v4.view.ViewPager>
AND FINALLY, MY XML FOR THE GRIDVIEW:
<?xml version="1.0" encoding="utf-8"?>
<GridView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/lbl_gridView"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_gravity="center_horizontal"
android:layout_margin="0px"
android:alwaysDrawnWithCache="false"
android:animateLayoutChanges="false"
android:background="#android:color/white"
android:columnWidth="130dp"
android:drawSelectorOnTop="true"
android:horizontalSpacing="#dimen/activity_horizontal_margin"
android:listSelector="#null"
android:numColumns="auto_fit"
android:padding="#dimen/activity_horizontal_margin"
android:scrollbarStyle="insideOverlay"
android:smoothScrollbar="true"
android:stretchMode="none"
android:verticalSpacing="#dimen/activity_vertical_margin" >
</GridView>
Use CENTER_INSIDE a scale type for your image view.
super.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
another thing that I would change is the way you handle the column width. I would remove the android:numColumns="auto_fit". On the other hand you know that the number of columns is the cealing of WidthViewGrid/desiderePicSize, where the most common case for WidthViewGrid is the screen's width. On then programmatically you can set number of columns of your GridView with setNumberOfColumns
I would say:
A. After calling setColumnWidth call the gridview.invalidate()
B. After returning from the change preference - call adapter's notifyDataSetChanged in order to make sure it re-creates the views in the adapter (i.e. getView will be called)
I have a Fragment with a method setName() that changes an EditText text, by means of the setText function.
What is the best way to call that method from the activity that hosts that fragment by means of a ViewPager?
In other words, how can I access a Fragment's methods (which change that fragment's layout, for example) from the Activity that hosts that fragment by means of a ViewPager?
I am asking this because I have tried several ways, but always with errors.
Best way to do this, just call
CallingFragmentName fragment = (CallingFragmentName) viewPager
.getAdapter()
.instantiateItem(viewPager, viewPager.getCurrentItem());
It will re-instantiate your calling Fragment, so that it will not throw null pointer exception.
I know this is a little late, but I ran into the same problem and maybe it will help others if you already solved it.
The first problem I found with ViewPager is that it is almost impossible to get a reference to a fragment. The fragments are created dynamically in getItem() and therefore you can't set an ID and they are automatically re-taged by the swicher, so you can't find it by tag either. There are some ways out there to do it, but they are all workarounds. (Update data in ListFragment as part of ViewPager)
The way I solved it was using essentially a double Callback. Fragment A has an interface implemented by the Main Activity, the Main Activity has a interface implemented by Fragment B. On e.g. a button clink in Fragment A the callback function in Main Activity is called, which than in turn calls the callback in Fragment B. Look at the code below. I hope I posted everything and it will help. btw, I have only tried this with a ViewPager, but I assume it would work with any sort of Fragment communication.
Main Avtivity java:
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.view.ViewPager;
public class MainActivity extends FragmentActivity implements FragmentA.Caller {
SectionsPagerAdapter mSectionsPagerAdapter;
ViewPager mViewPager;
PassCallToB passOnToB = null;
FragmentManager myManager = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());
mViewPager = (ViewPager) findViewById(R.id.pager);
mViewPager.setAdapter(mSectionsPagerAdapter);
}
public class SectionsPagerAdapter extends FragmentPagerAdapter {
public SectionsPagerAdapter(FragmentManager fm) {
super(fm);
MyManager = fm;
}
#Override
public Fragment getItem(int position) {
Fragment fragment = null;
if(position == 0) {
fragment = new FragmentA();
} else if (position == 1) {
fragment = new FragmentB();
passOnToB = (PassCallToB)fragment;
}
return fragment;
}
#Override
public int getCount() {
return 2;
}
#Override
public CharSequence getPageTitle(int position) {
switch (position) {
case 0:
return "Frag A";
case 1:
return "Frag B";
}
return null;
}
public void setCallback() {
List<Fragment> frags = myManager.getFragments();
for(Fragment fragment : frags) {
if(fragment instanceof FragmentB){
passOnToB = (PassCallToB)fragment;
}
}
}
}
public interface PassCallToB {
public void passItOn();
}
#Override
public void CallB() {
if(passOnToB instanceof Fragment)
passOnToB.passItOn();
else {
mSectionsPagerAdapter.setCallback();
passOnToB.passItOn();
}
}
}
Main Activity xml:
<android.support.v4.view.ViewPager xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity" >
<android.support.v4.view.PagerTitleStrip
android:id="#+id/pager_title_strip"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="top"
android:background="#33b5e5"
android:paddingBottom="4dp"
android:paddingTop="4dp"
android:textColor="#fff" />
</android.support.v4.view.ViewPager>
Fragment A java:
import android.app.Activity;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.Button;
public class FragmentA extends Fragment {
Button btnCallB = null;
Caller listener = null;
public FragmentA() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle inState) {
View rootView = inflater.inflate(R.layout.fragment_a, container, false);
btnCallB = (Button)rootView.findViewById(R.id.btnCallB);
btnCallB.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View view) {
listener.CallB();
}
});
return rootView;
}
public interface Caller {
public void CallB();
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
if (activity instanceof FragmentActivity) {
listener = (Caller) activity;
} else {
throw new ClassCastException(activity.toString() + " must implemenet listener");
}
}
#Override
public void onDetach() {
super.onDetach();
listener = null;
}
}
Fragment A xml:
<RelativeLayout 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" >
<TextView
android:id="#+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:text="This is Fragment A" />
<Button
android:id="#+id/btnCallB"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_below="#+id/textView1"
android:text="Call Fragment B" />
</RelativeLayout>
Fragment B Java:
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
public class FragmentB extends Fragment implements MainActivity.PassCallToB {
public FragmentB() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle inState) {
View rootView = inflater.inflate(R.layout.fragment_b, container, false);
return rootView;
}
#Override
public void passItOn() {
Toast.makeText(getActivity(), "Hello from B", Toast.LENGTH_SHORT).show();
}
}
Fragment B xml:
<RelativeLayout 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" >
<TextView
android:id="#+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:text="This is Fragment B" />
</RelativeLayout>
You can access public methods within the fragments held by your ViewPager. You need to either (1) store a reference to the Fragment when you create it and add it to the list that will back your pager adapter or (2) you need to get a reference to the fragment from the pager adapter itself. For example:
Fragment fragmentA = null; //instance variable
fragmenA = new Fragment(); //whereever you instantiate your fragment
If your method is
public void setName(String args){
//do something
}
all you would do is call that method from the reference to the fragment held by your ViewPager
fragmentA.setName(args);
You pass whatever arguments you need just like calling a regular method. Note this ONLY works if you are calling a method within a fragment from its containing ViewPager or FragmentActivity. If you want to do the reverse, fragment to activity, you need to use an inerface.
Fragment
private static FragmentName instance;
public static synchronized FragmentName getInstance()
{
return instance;
}
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
instance=this;
....
}
public void methodName()
{...}
Activity
FragmentName.getInstance().methodName();
I am having some trouble with this piece of code:
MyAdapter.java:
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentStatePagerAdapter;
public static class MyAdapter extends FragmentStatePagerAdapter {
public MyAdapter(FragmentManager fm) {
super(fm);
}
#Override
public int getCount() {
return 3;
}
#Override
public Fragment getItem(int position) {
switch (position) {
case 0: return new SpeedZonesFragment();
case 1: return new DistanceFragment();
case 2: return new FuelConsumptionFragment();
default: return null;
}
}
}
MainActivity.java:
import android.app.ActionBar;
import android.app.ActionBar.Tab;
import android.app.FragmentTransaction;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.support.v4.view.ViewPager;
public class MainActivity extends FragmentActivity {
private MyAdapter myAdapter;
private ViewPager myViewPager;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.view_pager);
myAdapter = new MyAdapter(getSupportFragmentManager());
myViewPager = (ViewPager) findViewById(R.id.pager);
myViewPager.setAdapter(myAdapter);
view_pager.xml:
<?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="vertical" >
<android.support.v4.view.ViewPager
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/view_pager" />
</LinearLayout>
My problem goes that every example i can find declares MyAdapter as a public static class however Eclipse raise the error: Illegal modifier for the class MyAdapter; only public, abstract & final are permitted.
If i try to run my code without the static it returns (from LogCat):
Caused by: java.lang.NullPointerException
02-02 23:13:26.076: E/AndroidRuntime(928):
at simcas.fartberegneren.MainActivity.onCreate(MainActivity.java:23)
EDIT: Line 23 is the line myViewPager.setAdapter(myAdapter);
Clearly i am doing something wrong but i just can't locate it.
Firstly, no, static classes are only used for inner classes wherein the inner class's instances should not be tied to the outer class's instance. (That is, in an outer class, the static modified should not be used.)
Second, the null pointer is in the statement myViewPager.setAdapter(myAdapter);, as you deduced, meaning myViewPager is null. Therefore, the findViewById() is returning null (item not found):
myViewPager = (ViewPager) findViewById(R.id.pager);
Notice, in your XML: android:id="#+id/view_pager". The above line should then be:
myViewPager = (ViewPager) findViewById(R.id.view_pager);