具体的には、Activity に当然あるはずのメソッドが『存在しません』等表示されて、赤い線でエラーが発生したりします。 その他にも、ありえないエラー等あった場合は、メニューから『Invalidate Caches / Restart』を実行します 復旧に数分かかる結構長い処理になりますが、過去4,5回発生したトラブルは皆この処理で解決しています。
2016年08月08日
Android Studio で理解不能なエラーが出た時の対処方法 : Invalidate Caches / Restart
2016年08月01日
Android : TabHost のタブに下から上へのアニメーションを設定して、include で同一画面を使用するので 回転しないように AndroidMainfest で設定する
TabHost のタブの切り替わりがあまりにもそっけないので、slide_from_bottom.xml と slide_up.xml を作成して、下から上へと移動するアニメーションを設定しました。左右にしてもいいのですが、それだともう二種類のアニメーションが必要になりそうなので下から上にしました。 Interpolators の値(Android developer) ※ 参考にした記事 TabHost 内の各 TabSpec 内にある TextView の 端末回転時における保存と復帰 では、tab 毎に違った画面を定義しましたが、同じ画面を使用して工数が減るパターンとしてサンプルを作成しました。 ただ、この場合ですと、端末の回転による Activity の再作成で、Tab 内の EditText の保存と復帰が正しくされないので、AndroidMainfest で『回転しない』ように縦固定にしました。
public class MainActivity extends AppCompatActivity { private TabHost tabhost; private View pview; private View tabView1; private View tabView2; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // TabHost のインスタンスを取得 tabhost = (TabHost)MainActivity.this.findViewById(R.id.tabHost); // タブ追加の前に必要な初期処理 tabhost.setup(); // タブ用のインスタンスを作成 TabHost.TabSpec tab1 = tabhost.newTabSpec("tab1"); // タイトル文字列を設定 tab1.setIndicator("タブ1の\nタイトル"); // このタブ内に表示するコンテンツを TabHost 画面内の FrameLayout // の中にあるうちのコンテンツのひとつを設定 tab1.setContent(R.id.linearLayout1); // TabHost に このタブを追加 tabhost.addTab(tab1); TabHost.TabSpec tab2 = tabhost.newTabSpec("tab2"); tab2.setIndicator("タブ2の\nタイトル"); tab2.setContent(R.id.linearLayout2); tabhost.addTab(tab2); TabHost.TabSpec tab3 = tabhost.newTabSpec("tab3"); tab3.setIndicator("タブ3の\nタイトル"); tab3.setContent(R.id.linearLayout3); tabhost.addTab(tab3); pview = tabhost.getCurrentView(); tabhost.setOnTabChangedListener(new TabHost.OnTabChangeListener() { @Override public void onTabChanged(String tabId) { View currentView = tabhost.getCurrentView(); int tab = tabhost.getCurrentTab(); Animation a_out = AnimationUtils.loadAnimation(MainActivity.this, R.anim.slide_up); Animation a_in = AnimationUtils.loadAnimation(MainActivity.this, R.anim.slide_from_bottom); pview.setAnimation(a_out); currentView.setAnimation(a_in); pview = currentView; } }); // tab1 内のボタン tabView1 = MainActivity.this.findViewById(R.id.linearLayout1); Button button1; button1 = (Button) tabView1.findViewById(R.id.button); button1.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { TextView tv; // 画面最上部の TextView tv = (TextView) MainActivity.this.findViewById(R.id.textView); // 最初のタブの EditText EditText editText = (EditText) tabView1.findViewById(R.id.editText); tv.setText(editText.getText().toString()); // tab1 のコンテンツを取得 tv = (TextView) tabView1.findViewById(R.id.textView2); tv.setText(editText.getText().toString()); } }); // tab2 内のボタン tabView2 = MainActivity.this.findViewById(R.id.linearLayout2); Button button2; button2 = (Button) tabView2.findViewById(R.id.button); button2.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { TextView tv; // 真ん中のタブの EditText EditText editText = (EditText) tabView2.findViewById(R.id.editText); // tab2 のコンテンツを取得 tv = (TextView) tabView2.findViewById(R.id.textView2); tv.setText(editText.getText().toString()); } }); } }
画面定義
<?xml version="1.0" encoding="utf-8"?> <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" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="lightbox.july.tabhostapplication.MainActivity"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/textView" android:textSize="30dp"/> <TabHost android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/tabHost" android:layout_below="@+id/textView"> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <TabWidget android:id="@android:id/tabs" android:layout_width="match_parent" android:layout_height="wrap_content"> </TabWidget> <FrameLayout android:id="@android:id/tabcontent" android:layout_width="match_parent" android:layout_height="match_parent"> <include android:id="@+id/linearLayout1" android:layout_width="match_parent" android:layout_height="wrap_content" layout="@layout/tab_entry"/> <include android:id="@+id/linearLayout2" android:layout_width="match_parent" android:layout_height="wrap_content" layout="@layout/tab_entry"/> <include android:id="@+id/linearLayout3" android:layout_width="match_parent" android:layout_height="wrap_content" layout="@layout/tab_entry"/> </FrameLayout> </LinearLayout> </TabHost> </RelativeLayout> <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:textAppearance="?android:attr/textAppearanceLarge" android:id="@+id/textView2" android:textSize="40dp"/> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="入力を表示" android:id="@+id/button"/> <EditText android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/editText"/> </LinearLayout>
※ 便宜的に二つの画面をまとめて表示しています AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?> <manifest package="lightbox.july.tabhostapplication" xmlns:android="http://schemas.android.com/apk/res/android"> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity" android:screenOrientation="portrait"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> </application> </manifest>
アニメーション
<?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@android:anim/accelerate_interpolator"> <translate android:fromYDelta="50%p" android:toYDelta="0" android:duration="160"/> <alpha android:fromAlpha="0.0" android:toAlpha="1.0" android:duration="160" /> </set> <?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android"> <translate android:fromYDelta="0" android:toYDelta="-50%p" android:duration="@android:integer/config_mediumAnimTime"/> <alpha android:fromAlpha="1.0" android:toAlpha="0.0" android:duration="200" /> </set>
※ 便宜上ひとつにして表示しています ※ 上が、slide_from_bottom.xml です
Android : TabHost 内の各 TabSpec 内にある TextView の 端末回転時における保存と復帰
TabHost そのものは、ひとつの Activity で処理する場合とても簡単ですが、Android Studio の デザインで各タブの画面を確認できるように、xml で3つの画面定義をして activity_main.xml で include で読み込んでいます。 include タグにはそれぞれ id を設定して、TabSpec のコンテンツとして渡せるようにもしてあります。 端末回転時の保存と復帰に関しても、一般的な onSaveInstanceState と onRestoreInstanceState を使用していますが、画面内のコンテンツの id のパターンを以下のようにして、サンプルコードを作成しています。 1) EditText と Button には全て違った id を設定する 2) TextView には同じ id を設定する 通常、EditText は内部で自動的に内容が保存されて復帰されますが、3つの inclide 内の id を同じにしてしまうと( つまり、ひとつの画面定義で3つの include を使いまわししてしまうと )、保存と復帰がうまくいかないようなので、3つの画面を作成して、別々の id を設定しています。 しかし、同じ id を持った画面を使いまわす事も想定して、TextView には同じ id を設定して、include の id でまず view を取得して、その中の TextView として取得するサンプルコードです。 ここでは、使用していませんが最近の Drawable の取得方法として Drawable drawable = ResourcesCompat.getDrawable(getResources(), R.drawable.□□□, null); そして、Tab にアイコンを表示させる方法について Icon in Tab is not showing up (StackOverflow) ※ 要するにカスタムなインジケータを作成するそうです
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // TabHost のインスタンスを取得 TabHost tabhost = (TabHost)MainActivity.this.findViewById(R.id.tabHost); // タブ追加の前に必要な初期処理 tabhost.setup(); // タブ用のインスタンスを作成 TabHost.TabSpec tab1 = tabhost.newTabSpec("tab1"); // タイトル文字列を設定 tab1.setIndicator("タブ1の\nタイトル"); // このタブ内に表示するコンテンツを TabHost 画面内の FrameLayout // の中にあるうちのコンテンツのひとつを設定 tab1.setContent(R.id.linearLayout1); // TabHost に このタブを追加 tabhost.addTab(tab1); TabHost.TabSpec tab2 = tabhost.newTabSpec("tab2"); tab2.setIndicator("タブ2の\nタイトル"); tab2.setContent(R.id.linearLayout2); tabhost.addTab(tab2); TabHost.TabSpec tab3 = tabhost.newTabSpec("tab3"); tab3.setIndicator("タブ3の\nタイトル"); tab3.setContent(R.id.linearLayout3); tabhost.addTab(tab3); // tab1 内のボタン Button button1; button1 = (Button) MainActivity.this.findViewById(R.id.button); button1.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { TextView tv; // 画面最上部の TextView tv = (TextView) MainActivity.this.findViewById(R.id.textView); // 最初のタブの EditText EditText editText = (EditText) MainActivity.this.findViewById(R.id.editText); tv.setText(editText.getText().toString()); // tab1 のコンテンツを取得 View tab1 = MainActivity.this.findViewById(R.id.linearLayout1); tv = (TextView) tab1.findViewById(R.id.textView2); tv.setText(editText.getText().toString()); } }); // tab2 内のボタン Button button2; button2 = (Button) MainActivity.this.findViewById(R.id.button2); button2.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { TextView tv; // 真ん中のタブの EditText EditText editText = (EditText) MainActivity.this.findViewById(R.id.editText2); // tab2 のコンテンツを取得 View tab2 = MainActivity.this.findViewById(R.id.linearLayout2); tv = (TextView) tab2.findViewById(R.id.textView2); tv.setText(editText.getText().toString()); } }); } @Override protected void onRestoreInstanceState(Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); TabHost tabhost = (TabHost)findViewById(R.id.tabHost); // タブ位置の復帰 int currentTab = savedInstanceState.getInt("CurrentTab"); if ( currentTab != 0l ) { tabhost.setCurrentTab(currentTab); } else { tabhost.setCurrentTab(0); } // 一番上の TextView を復帰 String textTop = savedInstanceState.getString("TextViewTop"); if (textTop != null ) { TextView tv; tv = (TextView) MainActivity.this.findViewById(R.id.textView); tv.setText(textTop); } // tab1 内の TextView を復帰 String text1 = savedInstanceState.getString("TextView1"); if (text1 != null ) { View tab1 = MainActivity.this.findViewById(R.id.linearLayout1); TextView tv = (TextView) tab1.findViewById(R.id.textView2); tv.setText(text1); } // tab2 内の TextView を復帰 String text2 = savedInstanceState.getString("TextView2"); if (text2 != null ) { View tab2 = MainActivity.this.findViewById(R.id.linearLayout2); TextView tv = (TextView) tab2.findViewById(R.id.textView2); tv.setText(text2); } } @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); // タブ位置の保存 TabHost tabhost = (TabHost)MainActivity.this.findViewById(R.id.tabHost); int currentTab = tabhost.getCurrentTab(); outState.putInt("CurrentTab", currentTab); // 一番上の TextView を保存 TextView tvTop = (TextView) MainActivity.this.findViewById(R.id.textView); outState.putString("TextViewTop", tvTop.getText().toString()); // tab1 のコンテンツを取得 View tab1 = MainActivity.this.findViewById(R.id.linearLayout1); TextView tv1 = (TextView) tab1.findViewById(R.id.textView2); outState.putString("TextView1", tv1.getText().toString()); // tab2 のコンテンツを取得 View tab2 = MainActivity.this.findViewById(R.id.linearLayout2); TextView tv2 = (TextView) tab2.findViewById(R.id.textView2); outState.putString("TextView2", tv2.getText().toString()); } }
画面定義
<?xml version="1.0" encoding="utf-8"?> <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" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="lightbox.july.tabhostapplication.MainActivity"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/textView" android:textSize="30dp"/> <TabHost android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/tabHost" android:layout_below="@+id/textView"> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <TabWidget android:id="@android:id/tabs" android:layout_width="match_parent" android:layout_height="wrap_content"> </TabWidget> <FrameLayout android:id="@android:id/tabcontent" android:layout_width="match_parent" android:layout_height="match_parent"> <include android:id="@+id/linearLayout1" android:layout_width="match_parent" android:layout_height="wrap_content" layout="@layout/tab_entry"/> <include android:id="@+id/linearLayout2" android:layout_width="match_parent" android:layout_height="wrap_content" layout="@layout/tab_entry2"/> <include android:id="@+id/linearLayout3" android:layout_width="match_parent" android:layout_height="wrap_content" layout="@layout/tab_entry3"/> </FrameLayout> </LinearLayout> </TabHost> </RelativeLayout>
3つの tab 毎の定義
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:textAppearance="?android:attr/textAppearanceLarge" android:id="@+id/textView2" android:textSize="40dp"/> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="入力を表示" android:id="@+id/button"/> <EditText android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/editText"/> </LinearLayout> <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:textAppearance="?android:attr/textAppearanceLarge" android:id="@+id/textView2" android:textSize="40dp"/> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="入力を表示" android:id="@+id/button2"/> <EditText android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/editText2"/> </LinearLayout> <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:textAppearance="?android:attr/textAppearanceLarge" android:id="@+id/textView2" android:textSize="40dp"/> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="なにもしない" android:id="@+id/button3"/> <EditText android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/editText3"/> </LinearLayout>
※ 便宜上3つにまとめていますが、xml ファイルはそれぞれ 作成しています
2016年07月26日
Android の 端末回転時の EditText と TextView の違い
そもそも、画面を回転させなければ良いと思うのですが、『回転』して、Activity が再作成されたにもかかわらず、EditText の内容が保持されるので、インターネットを探して、けっこうやっと見つけました。 [Android]画面回転時の挙動 結局、EditText 内で保存と復帰を行っているそうです(ソースを読まれたそうなので)。 この記事内では、『Parcelable』が良く解らないというくだりがありますが、こちらは Android 関係では良くお世話になる Y.A.M さんの記事にあります。 Android Parcelable を使ってクラスのメンバを一時保存 まあ、自分的には教える立場にあるので、できるだけ難解な通路は通りたく無いので Google Gson で 文字列で保存して、デシリアライズして復帰したいところです。Android 関連の情報は、インターネットで正確な情報を引っこ抜くのはかなり難易度が高く、整理できても結局初心者に伝えるほうに無理があるという結論に良く達します。さらに、回転しても Activity 破棄させない設定等もありますが、汎用的な一般向けアプリを作るプロでも無いのに必要とはとても思え無い内容です。結局全ての場合を検証した事を確認できるような記事は表には無いですし・・・ やはり、回転させないのが一番です。 そして、それを前提にして回転させた場合に消失する TextView の復帰方法としての onSaveInstanceState(Bundle) で保存し、 onRestoreInstanceState(Bundle) で読み出します。 を目で見てを確かめるのがいいと思います。 ちなみに、TabHost の中身を同じ画面で include したら、最後のタブの EditText の内容が全てのタブの EditText に復帰されました。 別々の画面だと、当然それぞれが正しく保存されます
2016年07月25日
ViewPager 内のイベントで設定した TextView の値を保持する Fragment 処理
MainActivity.javaFragment の中の onCreateView で表示する為に、ボタンをクリックした時に TextView にセットすると同時に Fragment の中に定義した static な変数にも保存しました。保存された値は、adapter から getItem で呼ばれた時に渡す新たなインスタンスを作成する時に、再度 putStringArray でセットし setArguments でフラグメントに保存されます。 このような処理をインターネットで探しても無かったので、こうしてみましたが一応は動いています。このベースは、Android Studio のテンプレートなので、getArguments().getInt(PAGE_POSTION) をいきなり使うのは問題無いと思います( テンプレートでそうなっていたので )。なので、その付加情報として配列を追加した形です。
import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentPagerAdapter; import android.support.v4.view.ViewPager; import android.support.v7.app.AppCompatActivity; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; public class MainActivity extends AppCompatActivity { private SectionsPagerAdapter adapter; private ViewPager view_pager; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); adapter = new SectionsPagerAdapter(getSupportFragmentManager()); view_pager = (ViewPager) findViewById(R.id.viewPager); view_pager.setOffscreenPageLimit(1); view_pager.setAdapter(adapter); } public class SectionsPagerAdapter extends FragmentPagerAdapter { public SectionsPagerAdapter(FragmentManager fm) { super(fm); } @Override public Fragment getItem(int position) { return PlaceholderFragment.newInstance(position); } @Override public int getCount() { return 4; } @Override public CharSequence getPageTitle(int position) { switch (position) { case 0: return "SECTION 1"; case 1: return "SECTION 2"; case 2: return "SECTION 3"; case 3: return "SECTION 4"; } return null; } } public static class PlaceholderFragment extends Fragment { private static final String PAGE_POSTION = "page_position"; private static final String SAVE_VIEW_TEXT = "save_view_text"; private static String text[] = new String[4]; public PlaceholderFragment() { } public static PlaceholderFragment newInstance(int pagePosition) { PlaceholderFragment fragment = new PlaceholderFragment(); Bundle args = new Bundle(); args.putInt(PAGE_POSTION, pagePosition); args.putStringArray("save_view_text", text); fragment.setArguments(args); return fragment; } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { final View rootView = inflater.inflate(R.layout.fragment_main, container, false); if (getArguments().getStringArray(SAVE_VIEW_TEXT)[getArguments().getInt(PAGE_POSTION)] != null) { TextView textView = (TextView) rootView.findViewById(R.id.section_label); textView.setText(getArguments().getStringArray(SAVE_VIEW_TEXT)[getArguments().getInt(PAGE_POSTION)]); } Button btn = (Button) rootView.findViewById(R.id.button); btn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { EditText et = (EditText) rootView.findViewById(R.id.editText); TextView textView = (TextView) rootView.findViewById(R.id.section_label); textView.setText(et.getText().toString()); text[getArguments().getInt(PAGE_POSTION)] = et.getText().toString(); } }); return rootView; } } }
setOffscreenPageLimit(1) を 3 にすれば、アクティビティが破棄されない場合は TextView の値を保持してくれます。ですが、メモリを無駄に使うと思うので、Fragment の setArguments で渡すほうがいいと思います。 activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <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" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="lightbox.july.viewpagerapplication.MainActivity"> <android.support.v4.view.ViewPager android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/viewPager" android:layout_alignParentTop="true" android:layout_alignParentStart="true"> <android.support.v4.view.PagerTitleStrip android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/view"/> </android.support.v4.view.ViewPager> </RelativeLayout>
PagerTitleStrip は、特別必要ではありませんが、ViewPager のタイトルをせっかく処理しているので使用しています。設置は、手作業で ViewPager の間に挟む必要がありました。カスタムビューなので、コンポーネントツリーではうまくいきませんでした。fragment_main.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"> <TextView android:id="@+id/section_label" android:layout_width="match_parent" android:layout_height="wrap_content" android:textSize="40dp" android:layout_marginBottom="10dp"/> <EditText android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/editText" android:layout_below="@+id/section_label" android:layout_alignParentStart="true"/> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="New Button" android:id="@+id/button" android:layout_below="@+id/editText" android:layout_alignParentStart="true"/> </RelativeLayout>
![]()
結局これを使えば、わりと簡単に横スクロールのページ処理ができますが、Android の テンプレートでは、FloatingActionButton が使用されていたので、それを使うとページ先頭とページ最後へのジャンプも簡単に実装できます。 ※ FloatingActionButton は、com.android.support:design を追加する必要があります
Snackbar によるイベント処理
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab); fab.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Snackbar.make(view, "ページ移動", Snackbar.LENGTH_LONG) .setAction("先頭", new View.OnClickListener() { @Override public void onClick(View v) { mViewPager.setCurrentItem(0); } }) .show(); } }); FloatingActionButton fab2 = (FloatingActionButton) findViewById(R.id.fab2); fab2.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Snackbar.make(view, "ページ移動", Snackbar.LENGTH_LONG) .setAction("最後", new View.OnClickListener() { @Override public void onClick(View v) { mViewPager.setCurrentItem(3); } }) .show(); } });
2016年07月11日
ExpandableListView を使用して、タップした時に明細データ表示する
本来は、2階層構造のデータを扱います。例えば、最初に一覧として表示されるのが掲示板のスレッドだとすると、タップするとその下にそのスレッドに投稿されたタイトルの一覧が開く、と言うような用途です。 ただ、そのようなテストデータを作成するのは結構面倒なので、まずは単純に掲示板の一つのスレッドのタイトル一覧を表示して、タップするとさらに投稿者と日付と本文を表示するようにしました。ExpandableListView にデータを表示するには、専用のアダプタか必要ですが、ここでは、abstract class BaseExpandableListAdapter を使用しています。ExpandableListView にこのアダプターを継承したクラスのインスタンスを引き渡すと、少なくとも、getGroupView と getChildView と getGroupCount と getChildrenCount を呼び出して正しく表示してくれます。 とても簡単に情報を多く効果的に読み取る事のできるリストビューを作成する事ができます。 Android Studio で BaseExpandableListAdapter を継承してクラスを作成すると、中が空の状態では赤い波線が表示されてエラーになります。しかし、この赤い波線をクリックすると現れる左側の赤いランプをクリックしてやると、以下のようになります。
ここで、implement methods を選択してやると、ダイアログで必要なメソッドが選択済みになるので、 OK すると展開されます。そして、最低限 getGroupView と getChildView と getGroupCount と getChildrenCount を実装して、必要ならば getGroup と getChild を実装して中で使用すると良いと思います。 MainActivity 部分
package lightbox.july.expandablelistviewsimpledata; import android.content.Context; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseExpandableListAdapter; import android.widget.ExpandableListView; import android.widget.TextView; import com.google.gson.Gson; import jp.android.work.Tools; public class MainActivity extends AppCompatActivity { private JsonDataList json; private MyExpandableListAdapter adapter; private ExpandableListView elv; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 開くリストビューのインスタンス elv = (ExpandableListView) MainActivity.this.findViewById(R.id.expandableListView); // JSON データ(文字列) String data_url = "https://lightbox.sakura.ne.jp/homepage/demo/data/csvtype/json.php"; Tools.callHttpGet(data_url, "utf-8", new Tools.OnAsyncTaskListener() { @Override public void onAsyncTaskListener(String s) { Gson gson = new Gson(); // デシリアライズ json = gson.fromJson(s, JsonDataList.class); adapter = new MyExpandableListAdapter(json); elv.setAdapter(adapter); } }); } // 開くリストビュー用のアダプタ private class MyExpandableListAdapter extends BaseExpandableListAdapter{ private JsonDataList json; public MyExpandableListAdapter(JsonDataList json) { this.json= json; } // ****************************** // 親データの表示 // ( 実際は データの subject ) // ****************************** @Override public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) { View rowView = convertView; if (rowView == null) { // 現在の View の取得 LayoutInflater inflater = (LayoutInflater) MainActivity.this.getSystemService (Context.LAYOUT_INFLATER_SERVICE); rowView = inflater.inflate(R.layout.group_view, null); } // Adapter にセットされているこのポジションの MyData を取得 JsonData data = (JsonData)adapter.getGroup(groupPosition); // group_view にデータをセット TextView tv = (TextView) rowView.findViewById(R.id.textView); tv.setText(data.subject); return rowView; } // ****************************** // 子データの表示 // ( 実際は データの 残りの項目 ) // ****************************** @Override public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) { View rowView = convertView; if (rowView == null) { // 現在の View の取得 LayoutInflater inflater = (LayoutInflater) MainActivity.this.getSystemService (Context.LAYOUT_INFLATER_SERVICE); rowView = inflater.inflate(R.layout.child_view, null); } // Adapter にセットされているこのポジションの MyData を取得 JsonData data = (JsonData)adapter.getGroup(groupPosition); // child_view にデータをセット TextView tv2 = (TextView) rowView.findViewById(R.id.textView2); tv2.setText(data.name); TextView tv3 = (TextView) rowView.findViewById(R.id.textView3); tv3.setText(data.datetime); TextView tv4 = (TextView) rowView.findViewById(R.id.textView4); tv4.setText(data.text); return rowView; } @Override public int getGroupCount() { return json.item.length; } @Override public int getChildrenCount(int groupPosition) { return 1; } @Override public Object getGroup(int groupPosition) { return json.item[groupPosition]; } @Override public Object getChild(int groupPosition, int childPosition) { return null; } @Override public long getGroupId(int groupPosition) { return 0; } @Override public long getChildId(int groupPosition, int childPosition) { return 0; } @Override public boolean hasStableIds() { return false; } @Override public boolean isChildSelectable(int groupPosition, int childPosition) { return false; } } private class JsonData { String text; String subject; String name; String datetime; } private class JsonDataList { JsonData[] item; } }
全てのソースコード
Seesaa の各ページの表示について
Seesaa の 記事がたまに全く表示されない場合があります。その場合は、設定> 詳細設定> ブログ設定 で 最新の情報に更新の『実行ボタン』で記事やアーカイブが最新にビルドされます。 Seesaa のページで、アーカイブとタグページは要注意です。タグページはコンテンツが全く無い状態になりますし、アーカイブページも歯抜けページはコンテンツが存在しないのにページが表示されてしまいます。 また、カテゴリページもそういう意味では完全ではありません。『カテゴリID-番号』というフォーマットで表示されるページですが、実際存在するより大きな番号でも表示されてしまいます。 ※ インデックスページのみ、実際の記事数を超えたページを指定しても最後のページが表示されるようです 対処としては、このようなヘルプ的な情報を固定でページの最後に表示するようにするといいでしょう。具体的には、メインの記事コンテンツの下に『自由形式』を追加し、アーカイブとカテゴリページでのみ表示するように設定し、コンテンツを用意するといいと思います。※ エキスパートモードで表示しています アーカイブとカテゴリページはこのように簡単に設定できますが、タグページは HTML 設定を直接変更して、以下の『タグページでのみ表示される内容』の記述方法で設定する必要があります
<% if:page_name eq 'archive' -%> アーカイブページでのみ表示される内容 <% /if %> <% if:page_name eq 'category' -%> カテゴリページでのみ表示される内容 <% /if %> <% if:page_name eq 'tag' -%> タグページでのみ表示される内容 <% /if %>この記述は、以下の場所で使用します![]()
|