use custom layout for board/toolbar relationship

Correct or not, this is my solution to the circular problem of how to
choose a vertical or horizontal toolbar before beginning the layout of
the board and toolbar. Adding a new custom container that holds the
board, toolbar(s) and tile exchange buttons and uses the ratio of its
own dimensions to choose which toolbar to show. Also drive toolbar
initialization from the layout process because when started from
BoardDelegate it now tries to install button listeners too early.
This commit is contained in:
Eric House 2016-08-16 11:01:46 -07:00
parent 4857ad6b26
commit 6da7c85dbf
9 changed files with 302 additions and 147 deletions

View file

@ -277,8 +277,8 @@ public final class R {
public static final int smart_robot=0x7f0c0044;
public static final int state=0x7f0c0051;
public static final int summary=0x7f0c0064;
public static final int tbar_parent_hor=0x7f0c0007;
public static final int tbar_parent_vert=0x7f0c0008;
public static final int tbar_parent_hor=0x7f0c0008;
public static final int tbar_parent_vert=0x7f0c0007;
public static final int text_item=0x7f0c0056;
public static final int text_item2=0x7f0c0057;
public static final int thumbnail=0x7f0c004e;

View file

@ -1,58 +1,57 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/board_root"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<LinearLayout android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:layout_weight="1"
>
<org.eehouse.android.xw4.BoardView
android:id="@+id/board_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:focusable="true"
android:clickable="true"
android:longClickable="true"
android:drawSelectorOnTop="false"
android:layout_gravity="center_horizontal"
android:background="@android:color/transparent"
/>
<!-- BoardContainer is a custom layout that among other things assumes
four children. Change the number of children and an assert will fire.
-->
<LinearLayout android:id="@+id/exchange_buttons"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:visibility="gone"
>
<Button android:id="@+id/exchange_commit"
android:text="@string/button_trade_commit"
style="@style/evenly_spaced_horizontal"
/>
<Button android:id="@+id/exchange_cancel"
android:text="@string/button_trade_cancel"
style="@style/evenly_spaced_horizontal"
/>
</LinearLayout>
<org.eehouse.android.xw4.BoardContainer
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/board_root"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<org.eehouse.android.xw4.BoardView
android:id="@+id/board_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:focusable="true"
android:clickable="true"
android:longClickable="true"
android:drawSelectorOnTop="false"
android:layout_gravity="center_horizontal"
android:background="@android:color/transparent"
/>
<!-- toolbar goes inside me -->
<HorizontalScrollView android:id="@+id/tbar_parent_hor"
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
/>
<LinearLayout android:id="@+id/exchange_buttons"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:visibility="gone"
>
<Button android:id="@+id/exchange_commit"
android:text="@string/button_trade_commit"
style="@style/evenly_spaced_horizontal"
/>
<Button android:id="@+id/exchange_cancel"
android:text="@string/button_trade_cancel"
style="@style/evenly_spaced_horizontal"
/>
</LinearLayout>
<!-- toolbar goes inside me -->
<!-- toolbar goes inside one of these -->
<ScrollView android:id="@+id/tbar_parent_vert"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
/>
</LinearLayout>
android:visibility="gone"
/>
<HorizontalScrollView android:id="@+id/tbar_parent_hor"
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
/>
</org.eehouse.android.xw4.BoardContainer>

View file

@ -4,7 +4,7 @@
android:id="@+id/toolbar"
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_height="wrap_content"
>
<ImageButton android:id="@+id/prevhint_button"
style="@style/toolbar_button"

View file

@ -0,0 +1,157 @@
/* -*- compile-command: "find-and-ant.sh debug install"; -*- */
/*
* Copyright 2009 - 2016 by Eric House (xwords@eehouse.org). All
* rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package org.eehouse.android.xw4;
import java.lang.ref.WeakReference;
import android.content.Context;
import android.util.AttributeSet;
import android.widget.LinearLayout;
import android.view.ViewGroup;
import android.view.View;
import junit.framework.Assert;
public class BoardContainer extends ViewGroup {
private static final int TBAR_PCT_HOR = 90;
private static final int TBAR_PCT_VERT = 85;
private static boolean s_isPortrait = true; // initial assumption
private static int s_width = 0;
private static int s_height = 0;
interface SizeChangeListener {
void sizeChanged( int width, int height, boolean isPortrait );
}
private static WeakReference<SizeChangeListener> s_scl;
public static void registerSizeChangeListener(SizeChangeListener scl)
{
s_scl = new WeakReference<SizeChangeListener>(scl);
callSCL();
}
public static boolean getIsPortrait() { return s_isPortrait; }
public BoardContainer( Context context, AttributeSet as ) {
super( context, as );
s_height = s_width = 0;
s_scl = null;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
final int childCount = getChildCount();
Assert.assertTrue( 4 == childCount );
int width = View.MeasureSpec.getSize( widthMeasureSpec );
int height = View.MeasureSpec.getSize( heightMeasureSpec );
if ( 0 != width || 0 != height ) {
setForPortrait( width, height );
}
int childState = 0;
for ( int ii = 0; ii < childCount; ii++ ) {
final View child = getChildAt( ii );
if ( GONE != child.getVisibility() ) {
// Measure the child.
measureChild( child, widthMeasureSpec, heightMeasureSpec );
childState =
combineMeasuredStates( childState, child.getMeasuredState() );
}
}
int maxHeight = getSuggestedMinimumHeight();
int maxWidth = getSuggestedMinimumWidth();
setMeasuredDimension( resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
resolveSizeAndState(maxHeight, heightMeasureSpec,
childState << MEASURED_HEIGHT_STATE_SHIFT) );
}
// In portrait mode, board gets BD_PCT of the vertical space. Otherwise it
// gets it all IFF the trade buttons aren't visible.
@Override
protected void onLayout( boolean changed, int left, int top,
int right, int bottom)
{
boolean haveTradeBar
= GONE != findViewById(R.id.exchange_buttons).getVisibility();
int boardHt = bottom - top;
if ( haveTradeBar || s_isPortrait ) {
boardHt = boardHt * TBAR_PCT_HOR / 100;
}
int boardWidth = right - left;
if ( !s_isPortrait ) {
boardWidth = boardWidth * TBAR_PCT_VERT / 100;
}
// layout the board
BoardView board = (BoardView)getChildAt( 0 );
board.layout( left, top, left + boardWidth, top + boardHt );
// The trade bar
if ( haveTradeBar ) {
LinearLayout exchButtons = (LinearLayout)getChildAt( 1 );
Assert.assertTrue( exchButtons.getId() == R.id.exchange_buttons );
exchButtons.layout( left, top + boardHt, right, bottom );
}
// Now one of the toolbars
View scrollView = getChildAt( s_isPortrait ? 3 : 2 );
Assert.assertTrue( GONE != scrollView.getVisibility() );
if ( s_isPortrait ) {
top += boardHt;
} else {
left += boardWidth;
}
scrollView.layout( left, top, right, bottom );
}
private void setForPortrait( final int width, final int height )
{
if ( height != s_height || width != s_width ) {
s_height = height;
s_width = width;
s_isPortrait = height > width;
findViewById( R.id.tbar_parent_hor )
.setVisibility( s_isPortrait ? VISIBLE : GONE );
findViewById( R.id.tbar_parent_vert )
.setVisibility( s_isPortrait ? GONE : VISIBLE );
callSCL();
}
}
private static void callSCL()
{
if ( 0 != s_width || 0 != s_height ) {
if ( null != s_scl ) {
SizeChangeListener scl = s_scl.get();
if ( null != scl ) {
scl.sizeChanged( s_width, s_height, s_isPortrait );
}
}
}
}
}

View file

@ -703,10 +703,8 @@ public class BoardDelegate extends DelegateBase
@Override
public void orientationChanged()
{
boolean isPortrait = isPortrait();
DbgUtils.logdf( "BoardDelegate.orientationChanged(isPortrait=%b)",
isPortrait );
positionToolbar( isPortrait );
DbgUtils.logdf( "BoardDelegate.orientationChanged()" );
initToolbar();
m_view.orientationChanged();
}
@ -716,13 +714,13 @@ public class BoardDelegate extends DelegateBase
setTitle( GameUtils.getName( m_activity, m_rowid ) );
}
private void positionToolbar( boolean isPortrait )
private void initToolbar()
{
// Wait until we're attached....
if ( null != findViewById( R.id.tbar_parent_hor ) ) {
if ( null == m_toolbar ) {
m_toolbar = new Toolbar( m_activity, this );
}
m_toolbar.setIsPortrait( isPortrait );
}
}
@ -1016,7 +1014,7 @@ public class BoardDelegate extends DelegateBase
case BUTTON_BROWSEALL_ACTION:
case BUTTON_BROWSE_ACTION:
String curDict = m_gi.dictName( m_view.getCurPlayer() );
View button = m_toolbar.getViewFor( Buttons.BUTTON_BROWSE_DICT );
View button = m_toolbar.getButtonFor( Buttons.BUTTON_BROWSE_DICT );
if ( Action.BUTTON_BROWSEALL_ACTION == action &&
DictsDelegate.handleDictsPopup( getDelegator(), button,
curDict, m_gi.dictLang ) ){
@ -2109,7 +2107,7 @@ public class BoardDelegate extends DelegateBase
setTitle();
}
positionToolbar( isPortrait() );
initToolbar();
populateToolbar();
adjustTradeVisibility();

View file

@ -107,7 +107,7 @@ public class BoardView extends View implements BoardHandler, SyncedDraw {
} else if ( XwJNI.board_containsPt( m_jniGamePtr, xx, yy ) ) {
handle( JNIThread.JNICmd.CMD_PEN_DOWN, xx, yy );
} else {
DbgUtils.logdf( "onTouchEvent(): in white space" );
DbgUtils.logdf( "BoardView.onTouchEvent(): in white space" );
}
break;
case MotionEvent.ACTION_MOVE:
@ -150,15 +150,15 @@ public class BoardView extends View implements BoardHandler, SyncedDraw {
@Override
protected void onMeasure( int widthMeasureSpec, int heightMeasureSpec )
{
// DbgUtils.logf( "onMeasure(width: %s, height: %s)",
// DbgUtils.logf( "BoardView.onMeasure(width: %s, height: %s)",
// MeasureSpec.toString( widthMeasureSpec ),
// MeasureSpec.toString( heightMeasureSpec ) );
if ( null != m_dims ) {
if ( m_boardDlgt.isPortrait() != (m_dims.height > m_dims.width) ) {
if ( BoardContainer.getIsPortrait() != (m_dims.height > m_dims.width) ) {
// square possible; will break above!
Assert.assertTrue( m_dims.height != m_dims.width );
DbgUtils.logdf( "onMeasure: discarding m_dims" );
DbgUtils.logdf( "BoardView.onMeasure: discarding m_dims" );
if ( ++m_dimsTossCount < 4 ) {
m_dims = null;
m_layoutWidth = m_layoutHeight = 0;
@ -276,7 +276,7 @@ public class BoardView extends View implements BoardHandler, SyncedDraw {
m_layoutHeight = height;
layoutDone = true;
}
DbgUtils.logdf( "layoutBoardOnce()=>%b", layoutDone );
DbgUtils.logdf( "BoardView.layoutBoardOnce()=>%b", layoutDone );
return layoutDone;
} // layoutBoardOnce
@ -339,7 +339,7 @@ public class BoardView extends View implements BoardHandler, SyncedDraw {
public void dimsChanged( BoardDims dims )
{
DbgUtils.logdf( "dimsChanged(%s)", dims.toString() );
DbgUtils.logdf( "BoardView.dimsChanged(%s)", dims.toString() );
m_dims = dims;
m_parent.runOnUiThread( new Runnable() {
public void run()

View file

@ -295,30 +295,6 @@ public class DelegateBase implements DlgClickNotify,
}
}
protected boolean isPortrait()
{
Point size = getContainerSize();
boolean result = size.x < size.y;
DbgUtils.logdf( "%s.isPortrait() => %b",
getClass().getSimpleName(), result );
return result;
}
private Point getContainerSize()
{
Point result = null;
if ( m_activity instanceof MainActivity ) {
result = ((MainActivity)m_activity).getFragmentSize();
} else {
Rect rect = new Rect();
m_rootView.getWindowVisibleDisplayFrame( rect );
result = new Point( rect.width(), rect.height() );
}
DbgUtils.logdf( "%s.getContainerSize(): width => %d, height => %d",
getClass().getSimpleName(), result.x, result.y );
return result;
}
private void runIfVisible()
{
if ( isVisible() ) {

View file

@ -218,21 +218,6 @@ public class MainActivity extends XWActivity
return handled;
}
protected Point getFragmentSize()
{
Rect rect = new Rect();
m_root.getWindowVisibleDisplayFrame( rect );
int width = rect.width();
int height = rect.height();
if ( null != m_isPortrait && m_isPortrait && height < width ) {
int tmp = width;
width = height;
height = tmp;
}
return new Point( width / Math.min( m_maxPanes, m_root.getChildCount() ),
height );
}
//////////////////////////////////////////////////////////////////////
// Delegator interface
//////////////////////////////////////////////////////////////////////

View file

@ -24,8 +24,13 @@ package org.eehouse.android.xw4;
import android.app.Activity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.HorizontalScrollView;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import junit.framework.Assert;
@ -33,7 +38,7 @@ import org.eehouse.android.xw4.DlgDelegate.Action;
import org.eehouse.android.xw4.DlgDelegate.HasDlgDelegate;
import org.eehouse.android.xw4.loc.LocUtils;
public class Toolbar {
public class Toolbar implements BoardContainer.SizeChangeListener {
public enum Buttons {
BUTTON_BROWSE_DICT(R.id.dictlist_button),
BUTTON_HINT_PREV(R.id.prevhint_button),
@ -54,58 +59,67 @@ public class Toolbar {
private DlgDelegate.HasDlgDelegate m_dlgDlgt;
private LinearLayout m_layout;
private boolean m_visible;
private Boolean m_isPortrait;
private ScrollView m_scrollVert;
private HorizontalScrollView m_scrollHor;
private Map<Buttons, Object> m_onClickListeners;
private Map<Buttons, Object> m_onLongClickListeners;
public Toolbar( Activity activity, HasDlgDelegate dlgDlgt )
{
m_activity = activity;
m_dlgDlgt = dlgDlgt;
m_scrollVert =
(ScrollView)activity.findViewById( R.id.tbar_parent_vert );
Assert.assertNotNull( m_scrollVert );
m_scrollHor =
(HorizontalScrollView)activity.findViewById( R.id.tbar_parent_hor );
Assert.assertNotNull( m_scrollHor );
BoardContainer.registerSizeChangeListener( this );
}
public void setVisible( boolean visible )
{
if ( m_visible != visible ) {
m_visible = visible;
doShowHide();
doShowHide( null );
}
}
public ImageButton getViewFor( Buttons index )
public ImageButton getButtonFor( Buttons index )
{
return (ImageButton)m_activity.findViewById( index.getResId() );
}
public void setListener( Buttons index, final int msgID, final int prefsKey,
final Action action )
public void setListener( Buttons index, final int msgID,
final int prefsKey, final Action action )
{
View.OnClickListener listener = new View.OnClickListener() {
if ( null == m_onClickListeners ) {
m_onClickListeners = new HashMap<Buttons, Object>();
}
m_onClickListeners.put( index, new View.OnClickListener() {
@Override
public void onClick( View view ) {
DbgUtils.logf( "Toolbar.setListener(): click on %s with action %s",
view.toString(), action.toString() );
m_dlgDlgt.makeNotAgainBuilder( msgID, prefsKey, action )
.show();
}
};
setListener( index, listener );
} );
}
public void setLongClickListener( Buttons index, final int msgID,
final int prefsKey, final Action action )
final int prefsKey, final Action action )
{
View.OnLongClickListener listener = new View.OnLongClickListener() {
if ( null == m_onLongClickListeners ) {
m_onLongClickListeners = new HashMap<Buttons, Object>();
}
m_onLongClickListeners.put( index, new View.OnLongClickListener() {
public boolean onLongClick( View view ) {
m_dlgDlgt.makeNotAgainBuilder( msgID, prefsKey, action )
.show();
return true;
}
};
setLongClickListener( index, listener );
}
protected void setIsPortrait( boolean isPortrait )
{
if ( null == m_isPortrait || m_isPortrait != isPortrait ) {
m_isPortrait = isPortrait;
doShowHide();
}
} );
}
public void update( Buttons index, boolean enable )
@ -117,19 +131,62 @@ public class Toolbar {
}
}
private void doShowHide()
// SizeChangeListener
public void sizeChanged( int width, int height, boolean isPortrait )
{
Assert.assertTrue( null != m_isPortrait );
DbgUtils.logf( "Toolbar.sizeChanged(isPortrait=%b)", isPortrait );
tryAddListeners( m_onClickListeners );
tryAddListeners( m_onLongClickListeners );
doShowHide( new Boolean(isPortrait) );
}
private void tryAddListeners( Map<Buttons, Object> map )
{
Iterator<Buttons> iter = map.keySet().iterator();
while ( iter.hasNext() ) {
Buttons key = iter.next();
Object listener = map.get( key );
if ( setListener( key, listener ) ) {
iter.remove();
}
}
}
private boolean setListener( Buttons index, Object listener )
{
ImageButton button = getButtonFor( index );
boolean success = null != button;
if ( success ) {
if ( listener instanceof View.OnClickListener ) {
button.setOnClickListener( (View.OnClickListener)listener );
} else if ( listener instanceof View.OnLongClickListener ) {
button.setOnLongClickListener( (View.OnLongClickListener)listener );
} else {
Assert.fail();
}
}
return success;
}
private void doShowHide( Boolean shouldBePortrait )
{
// BoardContainer owns which scroller we'll use, and signals its
// choice by setting their visibility. We use the one that's visible.
boolean isPortrait = View.GONE != m_scrollHor.getVisibility();
DbgUtils.logf( "Toolbar.doShowHide(): isPortrait: %b", isPortrait );
Assert.assertTrue( null == shouldBePortrait
|| shouldBePortrait.equals(isPortrait) );
if ( null == m_layout ) {
m_layout = (LinearLayout)LocUtils.inflate( m_activity, R.layout.toolbar );
} else {
((ViewGroup)m_layout.getParent()).removeView( m_layout );
}
m_layout.setOrientation( m_isPortrait ?
m_layout.setOrientation( isPortrait ?
LinearLayout.HORIZONTAL : LinearLayout.VERTICAL );
int id = m_isPortrait ? R.id.tbar_parent_hor : R.id.tbar_parent_vert;
ViewGroup scroller = (ViewGroup)m_activity.findViewById( id );
ViewGroup scroller = isPortrait ? m_scrollHor : m_scrollVert;
Assert.assertNotNull( scroller );
if ( null != scroller ) {
// Google's had reports of a crash adding second view
scroller.removeAllViews();
@ -138,21 +195,4 @@ public class Toolbar {
m_layout.setVisibility( m_visible? View.VISIBLE : View.GONE );
}
private void setListener( Buttons index, View.OnClickListener listener )
{
ImageButton button = getViewFor( index );
if ( null != button ) {
button.setOnClickListener( listener );
}
}
private void setLongClickListener( Buttons index,
View.OnLongClickListener listener )
{
ImageButton button = getViewFor( index );
if ( null != button ) {
button.setOnLongClickListener( listener );
}
}
}