I have 6 Fragments each with a RadioGroup containing 4 radio buttons they are all displayed on the same activity using a viewpager so the user can swipe through them.
i have been trying to implement an onCheckedChangedListener on each RadioGroup so I can know which radio button was checked in each fragment.
I've tried many things over the past days and my working solution is extremely long winded and there is surely a better way.
This is my activity that calls the fragments with the current solution in it
public class QuestionsActivity extends AppCompatActivity {
Toolbar toolbar;
ViewPager mViewPager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_questions);
// tool bar
toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
mViewPager = (ViewPager) findViewById(R.id.pager);
/** set the adapter for ViewPager */
mViewPager.setAdapter(new MyPagerAdapter(
getSupportFragmentManager()));
}
/**
* Defining a FragmentPagerAdapter class for controlling the fragments to be shown when user swipes on the screen.
*/
public class MyPagerAdapter extends FragmentPagerAdapter {
public MyPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
/** Show a Fragment based on the position of the current screen */
if (position == 0) {
return new Q1Fragment();
} else if (position == 1) {
return new Q2Fragment();
} else if (position == 2) {
return new Q3Fragment();
} else if (position == 3) {
return new Q4Fragment();
} else if (position == 4) {
return new Q5Fragment();
} else
return new Q6Fragment();
}
#Override
public int getCount() {
// Show 2 total pages.
return 6;
}
}
public void onRadio1Clicked(View view) {
boolean checked = ((RadioButton) view).isChecked();
switch (view.getId()) {
case R.id.radioButton1:
if (checked) {
Context context = getApplicationContext();
int duration = Toast.LENGTH_SHORT;
CharSequence text = "radio1";
Toast toast = Toast.makeText(context, text, duration);
toast.show();
}
break;
case R.id.radioButton2:
if (checked) {
Context context = getApplicationContext();
int duration = Toast.LENGTH_SHORT;
CharSequence text = "radio2";
Toast toast = Toast.makeText(context, text, duration);
toast.show();
}
break;
case R.id.radioButton3:
if (checked) {
Context context = getApplicationContext();
int duration = Toast.LENGTH_SHORT;
CharSequence text = "radio3";
Toast toast = Toast.makeText(context, text, duration);
toast.show();
}
break;
case R.id.radioButton4:
if (checked) {
Context context = getApplicationContext();
int duration = Toast.LENGTH_SHORT;
CharSequence text = "radio4";
Toast toast = Toast.makeText(context, text, duration);
toast.show();
}
break;
}
}
public void onRadio2Clicked(View view) {
boolean checked = ((RadioButton) view).isChecked();
switch (view.getId()) {
case R.id.radioButton1:
if (checked) {
Context context = getApplicationContext();
int duration = Toast.LENGTH_SHORT;
CharSequence text = "radio1";
Toast toast = Toast.makeText(context, text, duration);
toast.show();
}
break;
case R.id.radioButton2:
if (checked) {
Context context = getApplicationContext();
int duration = Toast.LENGTH_SHORT;
CharSequence text = "radio2";
Toast toast = Toast.makeText(context, text, duration);
toast.show();
}
break;
case R.id.radioButton3:
if (checked) {
Context context = getApplicationContext();
int duration = Toast.LENGTH_SHORT;
CharSequence text = "radio3";
Toast toast = Toast.makeText(context, text, duration);
toast.show();
}
break;
case R.id.radioButton4:
if (checked) {
Context context = getApplicationContext();
int duration = Toast.LENGTH_SHORT;
CharSequence text = "radio4";
Toast toast = Toast.makeText(context, text, duration);
toast.show();
}
break;
}
}
}
For each radio group i use android:onClick function in xml and create a new method for each one. above is just 2 of them.
Here is what a fragment looks like
<RadioGroup
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/radioGroup1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
>
<RadioButton
android:id="#+id/radioButton1"
android:layout_width="310dp"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:background="#drawable/border"
android:layout_marginTop="18dp"
android:padding="10dp"
android:text="#string/radio"
android:onClick="onRadio1Clicked"
/>
<RadioButton
android:id="#+id/radioButton2"
android:layout_width="310dp"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:background="#drawable/border"
android:layout_marginTop="18dp"
android:padding="10dp"
android:text="#string/radio1"
android:onClick="onRadio1Clicked"/>
<RadioButton
android:id="#+id/radioButton3"
android:layout_width="310dp"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:background="#drawable/border"
android:layout_marginTop="18dp"
android:padding="10dp"
android:text="#string/radio2"
android:onClick="onRadio1Clicked"/>
<RadioButton
android:id="#+id/radioButton4"
android:layout_width="310dp"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:background="#drawable/border"
android:layout_marginTop="18dp"
android:padding="10dp"
android:text="#string/radio3"
android:onClick="onRadio1Clicked"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="#string/submit"
android:layout_marginTop="13dp"
/>
</RadioGroup>
I have tried using a listener like in both the main activity and the fragment activity and i get null pointer exceptions for the second radiogroup.
Here is what a variation of the listener looks like, this is one i tried to use in a fragment activity
RadioGroup rg1 = (RadioGroup) getView().findViewById(R.id.radioGroup1);
rg1.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener()
{
#Override
public void onCheckedChanged (RadioGroup group,int checkedId){
Context context = getContext();
RadioButton thisButton = (RadioButton) getView().findViewById(checkedId);
Toast toast = Toast.makeText(context, thisButton.getText(), Toast.LENGTH_SHORT);
toast.show();
}
}
);
Whatever I do I cannot get the listener to work on fragment other than the first.
Any Ideas?
Just remove all android:onClick="onRadio1Clicked" from your layouts and setOnCheckedChangeListener code from your activity and implement it in fragments like in Q1Fragment override onViewCreated method and write code there, so on in other fragments.
in Q1Fragment :-
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
RadioGroup rg1 = (RadioGroup) view.findViewById(R.id.radioGroup1);
rg1.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener()
{
#Override
public void onCheckedChanged (RadioGroup group,int checkedId){
Context context = getContext();
RadioButton thisButton = (RadioButton) getView().findViewById(checkedId);
Toast toast = Toast.makeText(context, thisButton.getText(), Toast.LENGTH_SHORT);
toast.show();
}
}
);
}
in Q2Fragment :-
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
RadioGroup rg2 = (RadioGroup) view.findViewById(R.id.radioButton2);
rg2.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener()
{
#Override
public void onCheckedChanged (RadioGroup group,int checkedId){
Context context = getContext();
RadioButton thisButton = (RadioButton) getView().findViewById(checkedId);
Toast toast = Toast.makeText(context, thisButton.getText(), Toast.LENGTH_SHORT);
toast.show();
}
}
);
}
Related
so I have an application that is as follows:
login page where the user enters his credentials and can access the main app if his credentials are correct. and if he checks the remember me checkbox, his username and password will be saved in shared preferences so that he can directly go to the main app in the second time.
the main app has a tabbed layout with a viewpager. in one of the tabs, which is a fragment, I use a recyclerview to display data, that I get from a database, in rows.
now in each row there is a reply button that will show details corresponding to each row when clicked. the details will be shown in a new fragment.
so the point is that I managed to replace the tab's fragment with the new fragment using this code in the recyclerview's adapter:
public class recyclerviewAdapter : RecyclerView.Adapter
{
// Event handler for item clicks:
public event EventHandler<int> ItemClick;
List <summary_request> summary_Requests=new List<summary_request>();
//Context context;
public readonly stores_fragment context;
public recyclerviewAdapter(stores_fragment context, List<summary_request> sum_req)
{
this.context = context;
summary_Requests = sum_req;
}
public override RecyclerView.ViewHolder
OnCreateViewHolder(ViewGroup parent, int viewType)
{
View itemView = LayoutInflater.From(parent.Context).
Inflate(Resource.Layout.recycler_view_data, parent, false);
recyclerview_viewholder vh = new recyclerview_viewholder(itemView, OnClick);
return vh;
}
public override void
OnBindViewHolder(RecyclerView.ViewHolder holder, int position)
{
recyclerview_viewholder vh = holder as recyclerview_viewholder;
vh.by_user.Text = summary_Requests[position].By;
vh.warehousename.Text = summary_Requests[position].warehousename;
vh.project.Text = summary_Requests[position].project;
vh.operations_note.Text = summary_Requests[position].destination_Note;
vh.source_Note.Text = summary_Requests[position].source_Note;
vh.stockType.Text = summary_Requests[position].stockType;
vh.requestStatus.Text = summary_Requests[position].requestStatus;
vh.reply.Click += delegate
{
summary_detail_req fragment = new summary_detail_req();
var fm = context.FragmentManager.BeginTransaction();
fm.Replace(Resource.Id.frameLayout1, fragment);
fm.AddToBackStack(null);
fm.Commit();
int nb = context.FragmentManager.BackStackEntryCount;
Toast.MakeText(context.Context, nb.ToString(), ToastLength.Long).Show();
};
}
private void Reply_Click(object sender, EventArgs e)
{
Toast.MakeText(context.Context, "reply" , ToastLength.Long).Show();
}
public override int ItemCount
{
get { return summary_Requests.Count; }
}
// Raise an event when the item-click takes place:
void OnClick(int position)
{
if (ItemClick != null)
ItemClick(this, position);
}
}
but my context.FragmentManager.BackStackEntryCount remain zero! I don't get it. in my main activity, I am using this code for the backpress function:
stores_fragment.recyclerviewAdapter adapter;
public override void OnBackPressed()
{
string userName = pref.GetString("Username", String.Empty);
string password = pref.GetString("Password", String.Empty);
if (userName != String.Empty || password != String.Empty && adapter.context.FragmentManager.BackStackEntryCount == 0)
{
this.FinishAffinity();
}
else
base.OnBackPressed();
}
but i'm not getting what i want. this function is getting me out of the whole app.the first part of the if statement is because without it, when the I press the back button from the main activity it takes me back to the login page and I don't want that.
my question is what should I do to manage my fragments and the backpress function?
thanks in advance.
so the point is that I managed to replace the tab's fragment with the new fragment using this code in the recyclerview's adapter
According to your description, you want to open another fragment from recyclerview Button.click, if yes, please take a look the following code:
on OnBindViewHolder
int selectedindex;
// Fill in the contents of the photo card (invoked by the layout manager):
public override void
OnBindViewHolder(RecyclerView.ViewHolder holder, int position)
{
selectedindex =position;
PhotoViewHolder vh = holder as PhotoViewHolder;
// Set the ImageView and TextView in this ViewHolder's CardView
// from this position in the photo album:
vh.Image.SetImageResource(mPhotoAlbum[position].PhotoID);
vh.Caption.Text = mPhotoAlbum[position].Caption;
vh.btnreply.Click += Btnreply_Click;
}
To show detailed activity. MainActivity is the current activity for recyclerview.
private void Btnreply_Click(object sender, EventArgs e)
{
Showdetailed(selectedindex);
}
private void Showdetailed(int position)
{
var intent = new Intent();
intent.SetClass(MainActivity.mac, typeof(DetailsActivity));
intent.PutExtra("selectedid", position);
MainActivity.mac.StartActivity(intent);
}
The detailedactivity.cs:
public class DetailsActivity : Activity
{
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
// Create your application here
var index = Intent.Extras.GetInt("selectedid", 0);
var details = DetailsFragment.NewInstance(index); // Details
var fragmentTransaction = FragmentManager.BeginTransaction();
fragmentTransaction.Add(Android.Resource.Id.Content, details);
fragmentTransaction.Commit();
}
}
The DetailsFragment.cs:
public class DetailsFragment : Fragment
{
public int ShownPlayId => Arguments.GetInt("selectedid", 0);
public static DetailsFragment NewInstance(int index)
{
var detailsFrag = new DetailsFragment { Arguments = new Bundle() };
detailsFrag.Arguments.PutInt("selectedid", index);
return detailsFrag;
}
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
// Use this to return your custom view for this Fragment
// return inflater.Inflate(Resource.Layout.YourFragment, container, false);
if (container == null)
{
// Currently in a layout without a container, so no reason to create our view.
return null;
}
var scroller = new ScrollView(Activity);
var text = new TextView(Activity);
var padding = Convert.ToInt32(TypedValue.ApplyDimension(ComplexUnitType.Dip, 4, Activity.Resources.DisplayMetrics));
text.SetPadding(padding, padding, padding, padding);
text.TextSize = 24;
Photo photo =PhotoAlbum.mBuiltInPhotos[ShownPlayId];
text.Text = photo.Caption;
scroller.AddView(text);
return scroller;
}
}
About implementing fragment, you can take a look:
https://learn.microsoft.com/en-us/samples/xamarin/monodroid-samples/fragmentswalkthrough/
In the example bellow - window with Panel grows till it reaches browser window limits.
After that Panel content becomes scrollable but no scrollbar appear.
If I set Panel to fixed size scrollbar appear as per documentation.
Can someone suggest Vaadin pattern to implement scrollbars when Window or Panel size reaches browser window limit?
public class DemoUI extends UI {
#Override
protected void init(VaadinRequest vaadinRequest) {
VerticalLayout layout = new VerticalLayout();
layout.setSizeFull();
Button demo1 = new Button("Question");
demo1.addClickListener(new Button.ClickListener() {
#Override
public void buttonClick(Button.ClickEvent clickEvent) {
Window window = new Window();
window.setClosable(true);
window.center();
window.setResizable(true);
window.setModal(false);
window.setContent(new UndefWindow(window));
addWindow(window);
}
});
layout.addComponent(demo1);
setContent(layout);
}
}
public class UndefWindow extends VerticalLayout {
private Window window;
public UndefWindow(Window window){
this.window = window;
Panel panel = new Panel();
VerticalLayout layout = new VerticalLayout();
Button add10 =new Button("Add 10");
add10.addClickListener(new Button.ClickListener() {
#Override
public void buttonClick(Button.ClickEvent clickEvent) {
for (int i=0; i <10; i++){
layout.addComponent(new Label("Label: "+i));
}
window.center();
}
});
layout.addComponent(add10);
panel.setContent(layout);
addComponent(panel);
setExpandRatio(panel, 1.0f);
}
}
EDIT: I was able to achieve this with SizeReporter addon - code bellow
SizeReporter
public class UndefWindow extends VerticalLayout {
private Window window;
VerticalLayout root;
int rootHeight;
public UndefWindow(Window window, VerticalLayout root){
this.window = window;
this.root = root;
Panel panel = new Panel();
// panel.setHeight("400px");
SizeReporter sizeReporterRoot = new SizeReporter(root);
sizeReporterRoot.addResizeListener(new ComponentResizeListener() {
#Override
public void sizeChanged(ComponentResizeEvent event) {
System.out.println("Root size: " + event.getWidth() + " x " + event.getHeight());
rootHeight = event.getHeight();
}
});
SizeReporter sizeReporter = new SizeReporter(panel);
sizeReporter.addResizeListener(new ComponentResizeListener() {
#Override
public void sizeChanged(ComponentResizeEvent event) {
System.out.println("Panel size: " + event.getWidth() + " x " + event.getHeight());
if(event.getHeight()>rootHeight){
window.setHeight(rootHeight-60, Unit.PIXELS);
//window.center();
panel.setHeight(rootHeight-60, Unit.PIXELS);
}else
window.center();
}
});
VerticalLayout layout = new VerticalLayout();
Button add10 =new Button("Add 10");
add10.addClickListener(new Button.ClickListener() {
#Override
public void buttonClick(Button.ClickEvent clickEvent) {
for (int i=0; i <10; i++){
layout.addComponent(new Label("Label: "+i));
}
}
});
addComponent(add10);
panel.setContent(layout);
addComponent(panel);
}
}
You have a bit overly complicated thinking in your Window content. In Vaadin, Window itself extends Panel, so you do not need additional Panel there at all. So I stripped it away. Also in order to layouts to work properly, you want to have your VerticalLayout height undefined, i.e. to grow until browser window size is met. In order to Panel scrolling to work in that scenario, there needs to be some content of defined height, in this case it is the Label, so I set defined height to Label. Then adding labels, will add size of the VerticalLayout, and once browser height is exceeded, Panel gets scroll bars.
public class MyUI extends UI {
#Override
protected void init(VaadinRequest vaadinRequest) {
VerticalLayout layout = new VerticalLayout();
layout.setSizeFull();
Button demo1 = new Button("Question");
demo1.addClickListener(new Button.ClickListener() {
#Override
public void buttonClick(Button.ClickEvent clickEvent) {
Window window = new Window("Window");
window.setClosable(true);
window.center();
window.setResizable(true);
window.setModal(false);
window.setContent(new UndefWindow(window));
addWindow(window);
}
});
layout.addComponent(demo1);
setContent(layout);
}
public class UndefWindow extends VerticalLayout {
private Window window;
public UndefWindow(Window window){
this.window = window;
Button add10 =new Button("Add 10");
add10.addClickListener(new Button.ClickListener() {
#Override
public void buttonClick(Button.ClickEvent clickEvent) {
for (int i=0; i <10; i++){
Label label = new Label("Label: "+i);
label.setHeight("38px");
addComponent(label);
}
window.center();
}
});
addComponent(add10);
}
}
}
so i managed to get a stock navigation drawer to work and the action bar title to change with the fragments selected. I've also managed to get the backstack working easy peasy.
What i can't figure out how to do is get the action bar title to change back with the back click. google documentations says to add a onBackStackChangedListener:
getSupportFragmentManager().addOnBackStackChangedListener(
new FragmentManager.OnBackStackChangedListener() {
public void onBackStackChanged() {
// Update your UI here.
}
});
but i'm at a lost where to place it? they say when i .commit to changes so i assumed it was placed after
if (id == R.id.nav_spatial_awareness) {
setTitle("Spatial Awareness");
SpatialAwareness spatialAwarenessFragment = new SpatialAwareness();
android.support.v4.app.FragmentManager spatialAwarenessManager = getSupportFragmentManager();
spatialAwarenessManager.beginTransaction()
.addToBackStack(null)
.replace(R.id.main_content_layout, spatialAwarenessFragment, spatialAwarenessFragment.getTag())
.commit();
but that didn't work, this is what i tried and all i get is red squigglies
if (id == R.id.nav_spatial_awareness) {
setTitle("Spatial Awareness");
SpatialAwareness spatialAwarenessFragment = new SpatialAwareness();
android.support.v4.app.FragmentManager spatialAwarenessManager = getSupportFragmentManager();
spatialAwarenessManager.beginTransaction()
.addToBackStack(null)
.replace(R.id.main_content_layout, spatialAwarenessFragment, spatialAwarenessFragment.getTag())
.commit();
getSupportFragmentManager().addOnBackStackChangedListener(
new FragmentManager.OnBackStackChangedListener() {
public void onBackStackChanged() {
setTitle("Spatial Awareness");
}
});
please help me noob
so i tried this
if (id == R.id.nav_spatial_awareness) {
setTitle("Spatial Awareness");
final SpatialAwareness spatialAwarenessFragment = new SpatialAwareness();
android.support.v4.app.FragmentManager spatialAwarenessManager = getSupportFragmentManager();
spatialAwarenessManager.beginTransaction()
.addToBackStack(null)
.replace(R.id.main_content_layout, spatialAwarenessFragment, "spatialAwarenessFragmentTag")
.commit();
getSupportFragmentManager().addOnBackStackChangedListener(
new FragmentManager.OnBackStackChangedListener() {
public void onBackStackChanged() {
android.app.Fragment currentBackStackFragment = getFragmentManager().findFragmentByTag("spatialAwarenessFragmentTag");
if(currentBackStackFragment instanceof SpatialAwareness){
setTitle("Spatial");
}
}
});
i gave my fragment a tag and then tried matching the instance and then changing the title, still no good :(
Yes, the solution as mentioned here is to add a FragmentManager.OnBackStackChangedListener to your activity's FragmentManager.
Here is an example from a project I worked on:
(I have a navigation drawer with 7 Fragments and the OverviewFragment is the initial one that opens when the MainActivity opens)
My MainActivity:
public class MainActivity extends AppCompatActivity
implements NavigationView.OnNavigationItemSelectedListener, FragmentManager.OnBackStackChangedListener {
...
#Override
protected void onCreate(Bundle savedInstanceState) {
...
NavigationView navigationView = findViewById(R.id.nav_view);
navigationView.setNavigationItemSelectedListener(this);
if (savedInstanceState == null) {
// open the default fragment
OverviewFragment fragment = new OverviewFragment();
getSupportFragmentManager().beginTransaction()
.add(R.id.fragment_container, fragment).commit();
setTitle(R.string.title_fragment_overview);
}
getSupportFragmentManager().addOnBackStackChangedListener(this);
}
#Override
public boolean onNavigationItemSelected(MenuItem item) {
// Handle navigation view item clicks here.
Fragment fragment = null;
switch (item.getItemId()) {
case R.id.nav_overview:
fragment = new OverviewFragment();
break;
case R.id.nav_schedule:
fragment = new ScheduleFragment();
break;
case R.id.nav_all_tasks:
fragment = new AllTasksFragment();
break;
case R.id.nav_announcements:
fragment = new AnnouncementFragment();
break;
case R.id.nav_my_courses:
fragment = new MyCoursesFragment();
break;
case R.id.nav_map:
fragment = new MapFragment();
break;
case R.id.nav_settings:
fragment = new SettingsFragment();
break;
default:
fragment = new OverviewFragment();
}
getSupportFragmentManager().beginTransaction()
.replace(R.id.fragment_container, fragment)
.addToBackStack(null)
.commit();
drawer.closeDrawer(GravityCompat.START);
return true;
}
#Override
public void onBackStackChanged() {
FragmentManager fragmentManager = getSupportFragmentManager();
Fragment currentFragment =
fragmentManager.findFragmentById(R.id.fragment_container);
if (currentFragment instanceof OverviewFragment) {
setTitle(R.string.title_fragment_overview);
navigationView.getMenu().findItem(R.id.nav_overview).setChecked(true);
}
else if (currentFragment instanceof ScheduleFragment) {
setTitle(R.string.title_fragment_schedule);
navigationView.getMenu().findItem(R.id.nav_schedule).setChecked(true);
}
else if (currentFragment instanceof AllTasksFragment) {
setTitle(R.string.title_fragment_all_tasks);
navigationView.getMenu().findItem(R.id.nav_all_tasks).setChecked(true);
}
else if (currentFragment instanceof MyCoursesFragment) {
setTitle(R.string.title_fragment_my_courses);
navigationView.getMenu().findItem(R.id.nav_my_courses).setChecked(true);
}
else if (currentFragment instanceof AnnouncementFragment) {
setTitle(R.string.title_fragment_announcements);
navigationView.getMenu().findItem(R.id.nav_announcements).setChecked(true);
}
else if (currentFragment instanceof MapFragment) {
setTitle(R.string.title_fragment_map);
navigationView.getMenu().findItem(R.id.nav_map).setChecked(true);
}
else if (currentFragment instanceof SettingsFragment) {
setTitle(R.string.title_fragment_settings);
navigationView.getMenu().findItem(R.id.nav_settings).setChecked(true);
}
}
}
Here's the entire project: https://github.com/FCI-E-campus/fci-e-campus-android
I am newbie to android please help me, is their any possibility to add swipe action in vertical scroll view of activity screen.I am trying hard, but not getting...
I just converted vertical scroll view to Listview, Its works like a charm... Thanks to omid nazifi and wwyt, for more u can see this link Gesture in listview android
public class MainActivity extends ListActivity {
private OnTouchListener gestureListener;
private GestureDetector gestureDetector;
private int REL_SWIPE_MIN_DISTANCE;
private int REL_SWIPE_MAX_OFF_PATH;
private int REL_SWIPE_THRESHOLD_VELOCITY;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// As paiego pointed out, it's better to use density-aware measurements.
DisplayMetrics dm = getResources().getDisplayMetrics();
REL_SWIPE_MIN_DISTANCE = (int)(1.0f * dm.densityDpi / 160.0f + 0.5);
REL_SWIPE_MAX_OFF_PATH = (int)(250.0f * dm.densityDpi / 160.0f + 0.5);
REL_SWIPE_THRESHOLD_VELOCITY = (int)(200.0f * dm.densityDpi / 160.0f + 0.5);
ListView lv = getListView();
lv.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1,
m_Starbucks));
final GestureDetector gestureDetector = new GestureDetector(new MyGestureDetector());
View.OnTouchListener gestureListener = new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
return gestureDetector.onTouchEvent(event);
}};
lv.setOnTouchListener(gestureListener);
// Long-click still works in the usual way.
lv.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
String str = MessageFormat.format("Item long clicked = {0,number}", position);
Toast.makeText(MainActivity.this, str, Toast.LENGTH_SHORT).show();
return true;
}
});
/*lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position,
long id) {
String str = MessageFormat.format("Item #extra clicked = {0,number}", position);
Toast.makeText(MainActivity.this, str, Toast.LENGTH_SHORT).show();
}
});*/
}
// Do not use LitView.setOnItemClickListener(). Instead, I override
// SimpleOnGestureListener.onSingleTapUp() method, and it will call to this method when
// it detects a tap-up event.
private void myOnItemClick(int position, View v) {
String str = MessageFormat.format("Item clicked = {0,number}", position);
Toast.makeText(this, str, Toast.LENGTH_SHORT).show();
}
private void onLTRFling() {
Toast.makeText(this, "Left-to-right fling", Toast.LENGTH_SHORT).show();
}
private void onRTLFling() {
Toast.makeText(this, "Right-to-left fling", Toast.LENGTH_SHORT).show();
}
class MyGestureDetector extends SimpleOnGestureListener{
// Detect a single-click and call my own handler.
#Override
public boolean onSingleTapUp(MotionEvent e) {
View lv = (View)getListView();
int pos = ((AbsListView) lv).pointToPosition((int)e.getX(), (int)e.getY());
myOnItemClick(pos,lv);
return false;
}
#Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
if (Math.abs(e1.getY() - e2.getY()) > REL_SWIPE_MAX_OFF_PATH)
return false;
if(e1.getX() - e2.getX() > REL_SWIPE_MIN_DISTANCE &&
Math.abs(velocityX) > REL_SWIPE_THRESHOLD_VELOCITY) {
onRTLFling();
} else if (e2.getX() - e1.getX() > REL_SWIPE_MIN_DISTANCE &&
Math.abs(velocityX) > REL_SWIPE_THRESHOLD_VELOCITY) {
onLTRFling();
}
return false;
}
}
private static final String[] m_Starbucks = {
"Latte", "Cappuccino", "Caramel Macchiato", "Americano", "Mocha", "White Mocha",
"Mocha Valencia", "Cinnamon Spice Mocha", "Toffee Nut Latte", "Espresso",
"Espresso Macchiato", "Espresso Con Panna"
};
}
What will be the offset of the getHitRect after zooming and scrolling. Offset of the hit area after zoom can be calculated - (event.getX() / ZoomLayout.mScaleFactor + ZoomLayout.mClipBound.left) But i have not still figured out the offset of hit area after zooming and scrolling.
public class ZoomLayout extends RelativeLayout implements OnDoubleTapListener, OnGestureListener{
//ScalingFactor i.e. Amount of Zoom
static float mScaleFactor = 1.0f;
// Maximum and Minimum Zoom
private static float MIN_ZOOM = 1.0f;
private static float MAX_ZOOM = 2.0f;
//Different Operation to be used
private final int NONE_OPERATION=0;
private final int DRAG_OPERATION=1;
private final int ZOOM_OPERATION=2;
private float mWidth= 1280;
private float mHeight=800;
// Mode to select the operation
private int mode;
//Track X and Y coordinate of the finger when it first touches the screen
private float mInitialX = 0f;
private float mInitialY = 0f;
// Track the Bound of the Image after zoom to calculate the offset
static Rect mClipBound;
// mDetector to detect the scaleGesture for the pinch Zoom
private ScaleGestureDetector mDetector;
// mDoubleTapDetector to detect the double tap
private GestureDetector mDoubleTapDetector;
//Pivot point for Scaling
static float gx=0,gy=0;
boolean mdrag=false,mZoom=false;
public ZoomLayout(Context context, AttributeSet attrs) {
super(context, attrs);
setWillNotDraw(false);
mClipBound = new Rect();
// Intialize ScaleGestureDetector
mDetector = new ScaleGestureDetector(getContext(), new ZoomListener());
mDoubleTapDetector = new GestureDetector(context,this);
mDoubleTapDetector.setOnDoubleTapListener(this);
}
public ZoomLayout(Context context) {
super(context);
setWillNotDraw(false);
mClipBound = new Rect();
// Intialize ScaleGestureDetector
mDetector = new ScaleGestureDetector(getContext(), new ZoomListener());
mDoubleTapDetector = new GestureDetector(context,this);
mDoubleTapDetector.setOnDoubleTapListener(this);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
// Handles all type of motion-events possible
switch(event.getAction() ) {
case MotionEvent.ACTION_DOWN:
// Event occurs when the first finger is pressed on the Screen
Log.d("ZoomPrint", "Event: Action_Down " );
mInitialX = event.getX();
mInitialY = event.getY();
break;
case MotionEvent.ACTION_POINTER_DOWN:
//Event occurs when the second finger is pressed down
Log.d("ZoomPrint", "Event: Action_Pointer_Down " );
// If second finger is pressed on the screen with the first set the Mode to Zoom operation
mode=ZOOM_OPERATION;
break;
case MotionEvent.ACTION_POINTER_UP:
Log.d("ZoomPrint", "Event: Action_Pointer_UP " );
mdrag=true;
case MotionEvent.ACTION_UP:
//Event occurs when all the finger are taken of the screen
Log.d("ZoomPrint", "Event: Action_UP " );
//If all the fingers are taken up there will be no operation
mode = NONE_OPERATION;
mdrag=false;
break;
}
// give the event to the mDetector to get the scaling Factor
mDetector.onTouchEvent(event);
// give the event to the mDoubleTapDetector for the doubleTap
mDoubleTapDetector.onTouchEvent(event);
if(!mdrag)
invalidate();
return true;
}
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
onTouchEvent(ev);
return super.onInterceptTouchEvent(ev);
// return true;
}
#Override
public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
return super.invalidateChildInParent(location, dirty);
}
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b)
{
int count = getChildCount();
for(int i=0;i<count;i++){
View child = getChildAt(i);
if(child.getVisibility()!=GONE){
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams)child.getLayoutParams();
child.layout(
(int)(params.leftMargin ),
(int)(params.topMargin ),
(int)((params.leftMargin + child.getMeasuredWidth()) ),
(int)((params.topMargin + child.getMeasuredHeight()))
);
}
}
}
#Override
protected void dispatchDraw(Canvas canvas) {
//Save the canvas to set the scaling factor returned from detector
canvas.save(Canvas.MATRIX_SAVE_FLAG);
canvas.scale(mScaleFactor, mScaleFactor,gx,gy);
super.dispatchDraw(canvas);
mClipBound = canvas.getClipBounds();
canvas.restore();
}
private class ZoomListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
#Override
public boolean onScale(ScaleGestureDetector detector) {
// getting the scaleFactor from the detector
mScaleFactor *= detector.getScaleFactor(); // gives the scaling factor from the previous scaling to the current
// Log.d("ZoomPrint", "detector scaling Factor" + mScaleFactor);
gx = detector.getFocusX();
gy = detector.getFocusY();
// Limit the scale factor in the MIN and MAX bound
mScaleFactor= Math.max(Math.min(mScaleFactor, MAX_ZOOM),MIN_ZOOM);
// Log.d("ZoomPrint", "Bounded scaling Factor" + mScaleFactor);
/*//Force canvas to redraw itself only if the one event is to happen (say Zooming only ) else do not invalidate here for multi operations
As what we de for scrolling or panning will not reflect here. So we will add this in onDraw method
invalidate();*/
// Here we are only zooming so invalidate has to be done
// invalidate();
// requestLayout();
// we have handle the onScale
return true;
}
}
#Override
public boolean onDoubleTap(MotionEvent e) {
// Make the mScaleFactor to its normal value
if(mScaleFactor>1.0f)
{
mScaleFactor=1.0f;
}
// Force the canvas to redraw itself again as the changes has been occured.
invalidate();
requestLayout();
return false;
}
#Override
public boolean onDoubleTapEvent(MotionEvent e) {
// Log.d("ZoomPrint", "OnDoubleTapEvent");
return false;
}
#Override
public boolean onSingleTapConfirmed(MotionEvent e) {
// Log.d("ZoomPrint", "OnSingleTap");
return false;
}
#Override
public boolean onDown(MotionEvent e) {
// TODO Auto-generated method stub
return false;
}
#Override
public boolean onFling(MotionEvent e1, MotionEvent e2,
float velocityX, float velocityY) {
return false;
}
#Override
public void onLongPress(MotionEvent e) {
}
#Override
public boolean onScroll(MotionEvent e1, MotionEvent e2,
float distanceX, float distanceY) {
int distX= (int) distanceX, distY =(int) distanceY;
//Log.d("Print"," X " + this.mClipBound.left +" Y " + this.mClipBound.right + " b "+ this.mClipBound.bottom + " g" + this.mClipBound.top) ;
Log.d("Print", "Scroll X " + distanceX + " Y " + distanceY);
if(this.mClipBound.left<=0)
this.scrollTo(-280, 0);
else if(this.mClipBound.top<=0)
this.scrollTo(0, -250);
else if (this.mClipBound.right>=1047)
this.scrollTo(280, 0);
else if (this.mClipBound.bottom>=800)
this.scrollTo(0, 250);
else
this.scrollBy((int)distanceX,(int)distanceY);
return true;
}
#Override
public void onShowPress(MotionEvent e) {
}
#Override
public boolean onSingleTapUp(MotionEvent e) {
return false;
}
You need to translate the canvas during the scroll functionality. I have done it in this way in my code:
canvas.translate(getScrollX(), getScrollY());
getScrollX and Y are the amount of scroll.