mirror of
https://github.com/MatomoCamp/matomocamp-companion-android.git
synced 2024-09-19 16:13:46 +02:00
Replaced the ListView main menu with a ScrollView and an AdapterLinearLayout.
It makes the layout simpler and fixes focus issues in keypad navigation mode.
This commit is contained in:
parent
0c2ddc73e0
commit
b6cc5513db
8 changed files with 242 additions and 99 deletions
|
@ -43,10 +43,8 @@ import android.view.MenuItem;
|
|||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.animation.AnimationUtils;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.ListView;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.ScrollView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
|
@ -62,13 +60,14 @@ import be.digitalia.fosdem.fragments.LiveFragment;
|
|||
import be.digitalia.fosdem.fragments.MapFragment;
|
||||
import be.digitalia.fosdem.fragments.PersonsListFragment;
|
||||
import be.digitalia.fosdem.fragments.TracksFragment;
|
||||
import be.digitalia.fosdem.widgets.AdapterLinearLayout;
|
||||
|
||||
/**
|
||||
* Main entry point of the application. Allows to switch between section fragments and update the database.
|
||||
*
|
||||
* @author Christophe Beyls
|
||||
*/
|
||||
public class MainActivity extends ActionBarActivity implements ListView.OnItemClickListener {
|
||||
public class MainActivity extends ActionBarActivity {
|
||||
|
||||
private enum Section {
|
||||
TRACKS(TracksFragment.class, R.string.menu_tracks, R.drawable.ic_event_grey600_24dp, true),
|
||||
|
@ -225,23 +224,16 @@ public class MainActivity extends ActionBarActivity implements ListView.OnItemCl
|
|||
|
||||
// Setup Main menu
|
||||
mainMenu = findViewById(R.id.main_menu);
|
||||
ListView menuListView = (ListView) findViewById(R.id.main_menu_list);
|
||||
LayoutInflater inflater = LayoutInflater.from(this);
|
||||
View menuHeaderView = inflater.inflate(R.layout.header_main_menu, null);
|
||||
menuListView.addHeaderView(menuHeaderView, null, false);
|
||||
View menuFooterView = inflater.inflate(R.layout.footer_main_menu, null);
|
||||
menuFooterView.findViewById(R.id.settings).setOnClickListener(menuFooterClickListener);
|
||||
menuFooterView.findViewById(R.id.about).setOnClickListener(menuFooterClickListener);
|
||||
menuListView.addFooterView(menuFooterView, null, false);
|
||||
final AdapterLinearLayout sectionsList = (AdapterLinearLayout) findViewById(R.id.sections);
|
||||
menuAdapter = new MainMenuAdapter(getLayoutInflater());
|
||||
sectionsList.setAdapter(menuAdapter);
|
||||
mainMenu.findViewById(R.id.settings).setOnClickListener(menuFooterClickListener);
|
||||
mainMenu.findViewById(R.id.about).setOnClickListener(menuFooterClickListener);
|
||||
|
||||
LocalBroadcastManager.getInstance(this).registerReceiver(scheduleRefreshedReceiver, new IntentFilter(DatabaseManager.ACTION_SCHEDULE_REFRESHED));
|
||||
|
||||
menuAdapter = new MainMenuAdapter(inflater);
|
||||
menuListView.setAdapter(menuAdapter);
|
||||
menuListView.setOnItemClickListener(this);
|
||||
|
||||
// Last update date, below the menu
|
||||
lastUpdateTextView = (TextView) findViewById(R.id.last_update);
|
||||
// Last update date, below the list
|
||||
lastUpdateTextView = (TextView) mainMenu.findViewById(R.id.last_update);
|
||||
updateLastUpdateTime();
|
||||
|
||||
// Restore current section
|
||||
|
@ -254,7 +246,18 @@ public class MainActivity extends ActionBarActivity implements ListView.OnItemCl
|
|||
currentSection = Section.values()[savedInstanceState.getInt(STATE_CURRENT_SECTION)];
|
||||
}
|
||||
// Ensure the current section is visible in the menu
|
||||
menuListView.setSelection(currentSection.ordinal());
|
||||
sectionsList.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (sectionsList.getChildCount() > currentSection.ordinal()) {
|
||||
ScrollView mainMenuScrollView = (ScrollView) findViewById(R.id.main_menu_scroll);
|
||||
int requiredScroll = sectionsList.getTop()
|
||||
+ sectionsList.getChildAt(currentSection.ordinal()).getBottom()
|
||||
- mainMenuScrollView.getHeight();
|
||||
mainMenuScrollView.scrollTo(0, Math.max(0, requiredScroll));
|
||||
}
|
||||
}
|
||||
});
|
||||
updateActionBar();
|
||||
}
|
||||
|
||||
|
@ -452,12 +455,11 @@ public class MainActivity extends ActionBarActivity implements ListView.OnItemCl
|
|||
}
|
||||
};
|
||||
|
||||
private class MainMenuAdapter extends BaseAdapter {
|
||||
private class MainMenuAdapter extends AdapterLinearLayout.Adapter<Section> {
|
||||
|
||||
private final Section[] sections = Section.values();
|
||||
private final LayoutInflater inflater;
|
||||
private final int currentSectionForegroundColor;
|
||||
private final int currentSectionBackgroundColor;
|
||||
|
||||
public MainMenuAdapter(LayoutInflater inflater) {
|
||||
this.inflater = inflater;
|
||||
|
@ -468,7 +470,6 @@ public class MainActivity extends ActionBarActivity implements ListView.OnItemCl
|
|||
} finally {
|
||||
a.recycle();
|
||||
}
|
||||
currentSectionBackgroundColor = getResources().getColor(R.color.translucent_grey);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -481,72 +482,65 @@ public class MainActivity extends ActionBarActivity implements ListView.OnItemCl
|
|||
return sections[position];
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
return position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
if (convertView == null) {
|
||||
convertView = inflater.inflate(R.layout.item_main_menu, parent, false);
|
||||
convertView.setOnClickListener(sectionClickListener);
|
||||
}
|
||||
|
||||
Section section = getItem(position);
|
||||
convertView.setSelected(section == currentSection);
|
||||
|
||||
TextView tv = (TextView) convertView.findViewById(R.id.section_text);
|
||||
SpannableString sectionTitle = new SpannableString(getString(section.getTitleResId()));
|
||||
Drawable sectionIcon = getResources().getDrawable(section.getIconResId());
|
||||
int backgroundColor;
|
||||
if (section == currentSection) {
|
||||
// Special color for the current section
|
||||
//sectionTitle.setSpan(new StyleSpan(Typeface.BOLD), 0, sectionTitle.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
sectionTitle.setSpan(new ForegroundColorSpan(currentSectionForegroundColor), 0, sectionTitle.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
// We need to mutate the drawable before applying the ColorFilter, or else all the similar drawable instances will be tinted.
|
||||
sectionIcon.mutate().setColorFilter(currentSectionForegroundColor, PorterDuff.Mode.SRC_IN);
|
||||
backgroundColor = currentSectionBackgroundColor;
|
||||
} else {
|
||||
backgroundColor = Color.TRANSPARENT;
|
||||
}
|
||||
tv.setText(sectionTitle);
|
||||
tv.setCompoundDrawablesWithIntrinsicBounds(sectionIcon, null, null, null);
|
||||
tv.setBackgroundColor(backgroundColor);
|
||||
|
||||
return convertView;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
// Decrease position by 1 since the listView has a header view.
|
||||
Section section = menuAdapter.getItem(position - 1);
|
||||
if (section != currentSection) {
|
||||
// Switch to new section
|
||||
FragmentManager fm = getSupportFragmentManager();
|
||||
FragmentTransaction ft = fm.beginTransaction().setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
|
||||
Fragment f = fm.findFragmentById(R.id.content);
|
||||
if (f != null) {
|
||||
if (currentSection.shouldKeep()) {
|
||||
ft.detach(f);
|
||||
} else {
|
||||
ft.remove(f);
|
||||
private final View.OnClickListener sectionClickListener = new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
Section section = menuAdapter.getItem(((ViewGroup) view.getParent()).indexOfChild(view));
|
||||
if (section != currentSection) {
|
||||
// Switch to new section
|
||||
FragmentManager fm = getSupportFragmentManager();
|
||||
FragmentTransaction ft = fm.beginTransaction().setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
|
||||
Fragment f = fm.findFragmentById(R.id.content);
|
||||
if (f != null) {
|
||||
if (currentSection.shouldKeep()) {
|
||||
ft.detach(f);
|
||||
} else {
|
||||
ft.remove(f);
|
||||
}
|
||||
}
|
||||
}
|
||||
String fragmentClassName = section.getFragmentClassName();
|
||||
if (section.shouldKeep() && ((f = fm.findFragmentByTag(fragmentClassName)) != null)) {
|
||||
ft.attach(f);
|
||||
} else {
|
||||
f = Fragment.instantiate(this, fragmentClassName);
|
||||
ft.add(R.id.content, f, fragmentClassName);
|
||||
}
|
||||
ft.commit();
|
||||
String fragmentClassName = section.getFragmentClassName();
|
||||
if (section.shouldKeep() && ((f = fm.findFragmentByTag(fragmentClassName)) != null)) {
|
||||
ft.attach(f);
|
||||
} else {
|
||||
f = Fragment.instantiate(MainActivity.this, fragmentClassName);
|
||||
ft.add(R.id.content, f, fragmentClassName);
|
||||
}
|
||||
ft.commit();
|
||||
|
||||
currentSection = section;
|
||||
menuAdapter.notifyDataSetChanged();
|
||||
currentSection = section;
|
||||
menuAdapter.notifyDataSetChanged();
|
||||
}
|
||||
|
||||
drawerLayout.closeDrawer(mainMenu);
|
||||
}
|
||||
|
||||
drawerLayout.closeDrawer(mainMenu);
|
||||
}
|
||||
};
|
||||
|
||||
public static class AboutDialogFragment extends DialogFragment {
|
||||
|
||||
|
|
|
@ -0,0 +1,123 @@
|
|||
package be.digitalia.fosdem.widgets;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.DataSetObservable;
|
||||
import android.database.DataSetObserver;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
/**
|
||||
* Vertical LinearLayout populated by a special adapter.
|
||||
*
|
||||
* @author Christophe Beyls
|
||||
*/
|
||||
public class AdapterLinearLayout extends LinearLayout {
|
||||
|
||||
/**
|
||||
* Implement this Adapter to populate the layout.
|
||||
* Call notifyDataSetChanged() to update it.
|
||||
*/
|
||||
public static abstract class Adapter<T> {
|
||||
|
||||
private final DataSetObservable mDataSetObservable = new DataSetObservable();
|
||||
|
||||
private void registerDataSetObserver(DataSetObserver observer) {
|
||||
mDataSetObservable.registerObserver(observer);
|
||||
}
|
||||
|
||||
private void unregisterDataSetObserver(DataSetObserver observer) {
|
||||
mDataSetObservable.unregisterObserver(observer);
|
||||
}
|
||||
|
||||
public void notifyDataSetChanged() {
|
||||
mDataSetObservable.notifyChanged();
|
||||
}
|
||||
|
||||
public abstract int getCount();
|
||||
|
||||
public abstract T getItem(int position);
|
||||
|
||||
public abstract View getView(int position, View convertView, ViewGroup parent);
|
||||
}
|
||||
|
||||
private class AdapterLinearLayoutDataSetObserver extends DataSetObserver {
|
||||
@Override
|
||||
public void onChanged() {
|
||||
populateFromAdapter();
|
||||
}
|
||||
}
|
||||
|
||||
private Adapter mAdapter;
|
||||
private AdapterLinearLayoutDataSetObserver mDataSetObserver;
|
||||
|
||||
|
||||
public AdapterLinearLayout(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public AdapterLinearLayout(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
setOrientation(VERTICAL);
|
||||
}
|
||||
|
||||
public void setAdapter(Adapter adapter) {
|
||||
if (mAdapter == adapter) {
|
||||
return;
|
||||
}
|
||||
if (mAdapter != null && mDataSetObserver != null) {
|
||||
mAdapter.unregisterDataSetObserver(mDataSetObserver);
|
||||
}
|
||||
removeAllViews();
|
||||
mAdapter = adapter;
|
||||
if (adapter != null && mDataSetObserver != null) {
|
||||
populateFromAdapter();
|
||||
adapter.registerDataSetObserver(mDataSetObserver);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onAttachedToWindow() {
|
||||
super.onAttachedToWindow();
|
||||
mDataSetObserver = new AdapterLinearLayoutDataSetObserver();
|
||||
if (mAdapter != null) {
|
||||
populateFromAdapter();
|
||||
mAdapter.registerDataSetObserver(mDataSetObserver);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow();
|
||||
if (mAdapter != null) {
|
||||
mAdapter.unregisterDataSetObserver(mDataSetObserver);
|
||||
}
|
||||
mDataSetObserver = null;
|
||||
}
|
||||
|
||||
private void populateFromAdapter() {
|
||||
final Adapter adapter = mAdapter;
|
||||
final int currentCount = getChildCount();
|
||||
final int newCount = adapter.getCount();
|
||||
final int commonCount = Math.min(currentCount, newCount);
|
||||
// 1. Update common views
|
||||
for (int i = 0; i < commonCount; ++i) {
|
||||
final View currentView = getChildAt(i);
|
||||
final View newView = adapter.getView(i, currentView, this);
|
||||
if (currentView != newView) {
|
||||
// Edge case: View is not recycled
|
||||
removeViewAt(i);
|
||||
addView(newView, i);
|
||||
}
|
||||
}
|
||||
// 2a. Add missing views
|
||||
for (int i = commonCount; i < newCount; ++i) {
|
||||
addView(adapter.getView(i, null, this));
|
||||
}
|
||||
// 2b. Remove extra views (starting from the end to avoid array copies)
|
||||
for (int i = currentCount - 1; i >= commonCount; --i) {
|
||||
removeViewAt(i);
|
||||
}
|
||||
}
|
||||
}
|
13
app/src/main/res/drawable-v21/menu_item_background.xml
Normal file
13
app/src/main/res/drawable-v21/menu_item_background.xml
Normal file
|
@ -0,0 +1,13 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item>
|
||||
<selector>
|
||||
<item
|
||||
android:drawable="@color/translucent_grey"
|
||||
android:state_selected="true"/>
|
||||
<item android:drawable="@android:color/transparent"/>
|
||||
</selector>
|
||||
</item>
|
||||
<!-- Usage of theme attributes in an XML drawable is only allowed in API 21+ -->
|
||||
<item android:drawable="?attr/selectableItemBackground"/>
|
||||
</layer-list>
|
12
app/src/main/res/drawable/menu_item_background.xml
Normal file
12
app/src/main/res/drawable/menu_item_background.xml
Normal file
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item>
|
||||
<selector>
|
||||
<item
|
||||
android:drawable="@color/translucent_grey"
|
||||
android:state_selected="true"/>
|
||||
<item android:drawable="@android:color/transparent"/>
|
||||
</selector>
|
||||
</item>
|
||||
<item android:drawable="@drawable/abc_item_background_holo_light"/>
|
||||
</layer-list>
|
|
@ -1,24 +0,0 @@
|
|||
<?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="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<View
|
||||
style="@style/SeparatorLine"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:layout_marginTop="8dp"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/settings"
|
||||
style="@style/MainMenuItem"
|
||||
android:background="?selectableItemBackground"
|
||||
android:text="@string/settings"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/about"
|
||||
style="@style/MainMenuItem"
|
||||
android:background="?selectableItemBackground"
|
||||
android:text="@string/about"/>
|
||||
|
||||
</LinearLayout>
|
|
@ -1,7 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="@dimen/main_menu_padding"
|
||||
android:scaleType="center"
|
||||
android:src="@drawable/fosdem_title"/>
|
|
@ -35,19 +35,49 @@
|
|||
android:layout_width="260dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="left"
|
||||
android:background="@color/main_menu_background"
|
||||
android:orientation="vertical">
|
||||
|
||||
<ListView
|
||||
android:id="@+id/main_menu_list"
|
||||
<ScrollView
|
||||
android:id="@+id/main_menu_scroll"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
android:cacheColorHint="@android:color/transparent"
|
||||
android:divider="@null"
|
||||
android:dividerHeight="0dp"
|
||||
tools:listheader="@layout/header_main_menu"
|
||||
tools:listitem="@layout/item_main_menu"/>
|
||||
android:background="@color/main_menu_background">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="@dimen/main_menu_padding"
|
||||
android:scaleType="center"
|
||||
android:src="@drawable/fosdem_title"/>
|
||||
|
||||
<be.digitalia.fosdem.widgets.AdapterLinearLayout
|
||||
android:id="@+id/sections"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
<View
|
||||
style="@style/SeparatorLine"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:layout_marginTop="8dp"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/settings"
|
||||
style="@style/MainMenuItem"
|
||||
android:text="@string/settings"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/about"
|
||||
style="@style/MainMenuItem"
|
||||
android:text="@string/about"/>
|
||||
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/last_update"
|
||||
|
|
|
@ -61,6 +61,8 @@
|
|||
<!-- Styles -->
|
||||
|
||||
<style name="MainMenuItem" parent="TextAppearance.AppCompat.Body2">
|
||||
<item name="android:background">@drawable/menu_item_background</item>
|
||||
<item name="android:focusable">true</item>
|
||||
<item name="android:layout_width">match_parent</item>
|
||||
<item name="android:layout_height">@dimen/main_menu_item_height</item>
|
||||
<item name="android:gravity">left|center_vertical</item>
|
||||
|
|
Loading…
Reference in a new issue