Android Fragmentの中にFragmentを作る場合の注意

Android

Android Fragmentの中にFragmentを作る、いわゆる入れ子の状態でFragmentを表示させたいというので若干はまりましたのでメモしておきます。

書いて説明するのは難しいので、図を張っておきますが、ReviewFragmentというFragmentがまずReviewが必要な数だけ生成され、その中にReceiverFragmentが必要な数だけ生成され、またその中にSenderFragmentが必要な数生成される、という三層の入れ子になっています。

nested_fragment.png

で、普通に下記のように子供のFragmentを追加していたのですが、

 public class ReviewFragment extends Fragment {
 
    private ArrayList<Receiver> receiverArrayList;
    private ArrayList<Sender> senderArrayList;
 
    public ReviewFragment() {
 
    }
 
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }
 
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
 
        View v = inflater.inflate(R.layout.review_fragment, container, false);
        Bundle bundle = this.getArguments();
        Delivery delivery = bundle.getParcelable("delivery");
 
        int delivery_number = bundle.getInt("delivery_number") + 1;
        TextView delivery_info_tv = (TextView) v.findViewById(R.id.delivery_info);
        delivery_info_tv.setText(R.string.delivery + delivery_number);
 
        senderArrayList = delivery.getSenderList();
 
        receiverArrayList = delivery.getReceiverList();
        makeReceiverFragment(receiverArrayList);
 
        return v;
    }
 
 
    private void makeReceiverFragment(ArrayList<Receiver> receiverArrayList) {
 
 
        for (int count = 0; count < receiverArrayList.size(); count++) {
 
            FragmentManager fragMan = getFragmentManager();
            FragmentTransaction fragTransaction = fragMan.beginTransaction();
            Fragment review_receiver_fragment = new ReviewReceiverFragment();
 
            Receiver receiver = receiverArrayList.get(count);
            Bundle to_receiver_bundle = new Bundle();
            to_receiver_bundle.putParcelable("receiver", receiver);
 
            Log.d("ReviewFragment", receiver.getCompany_name());
 
            to_receiver_bundle.putParcelableArrayList("senderList", (ArrayList<Sender>) senderArrayList);
            to_receiver_bundle.putInt("delivery_number", count);
            review_receiver_fragment.setArguments(to_receiver_bundle);
 
            fragTransaction.add(R.id.each_receiver, review_receiver_fragment);
            fragTransaction.commit();
 
        }
 
    }
 }
 
 //以下、ほかのSenderFragment、ReceiverFragmentも同様に作成

そうすると、まずSenderFragmentが先に4つ表示されて、入れ子にならないのです。

下記のブログさんに答えが書いてありまして、

http://yuyakaido.hatenablog.com/entry/2014/02/16/230947

入れ子の場合、子供のFragmentを追加するには

 FragmentManager fragMan = getChildFragmentManager();

を利用しないといけなかったんですね。

そこを書き換えるだけで、解決です!

Android FragmentからActivity内のView要素を呼び出す

Android

Android FragmentからActivity内のView要素を呼び出す

MenuOverlyDialogFragmentというFragmentから、親のActivityの中のレイアウトのViewの要素を取得する方法です。

 public class MenuOverlyDialogFragment extends Fragment {
 
    @Override
    public View onCreateView(LayoutInflater inflater,
            ViewGroup container,
            Bundle savedInstanceState) {
        
        View v = inflater.inflate(R.layout.menu_overlay_dialog_fragment, 
                            container, false);
        return v;
    }
 
    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        
        begin_tracking_dialog_ll = (LinearLayout) getActivity().
                findViewById(R.id.begin_tracking_dialog_ll);
 
    }
 
 }

Android FragmentStatePagerAdapterを使ってレイアウトが共通のページを作る

Android

Android FragmentStatePagerAdapterを使ってレイアウトが共通のページを作るサンプルコード

FragmentStatePagerAdapterは便利な機能で、たとえば読ませたいコンテンツがあったとしたら、右、左のフリックで切り替えたり、共通のレイアウトを持たせたりすることができます。

「次へ」ボタンとかを配置したかったり、最後のページは「次へ」がないようにする、などのサンプルコードを書いておきます。
フリックだけだと先に進めない人がいるので、「次へ」ボタンをつけています。

このサンプルは、4ページのチュートリアルです。
ページの上にタイトルバー、中央に中身のコンテンツ、下の方に「次へ」と「スキップ」ボタンが配置されています。

 //元になるアクティビティ
 //TutorialBeforeStartActivity.java内
 
 public class TutorialBeforeStartActivity extends FragmentActivity  {
    private ViewPager viewPager;
    private Button skip;
 
    @Override
    public void onCreate(Bundle savedInstanceState) {
 
        super.onCreate(savedInstanceState);
        
        setContentView(R.layout.tutorial_before_start_fragment);
        viewPager = (ViewPager) findViewById(R.id.pager);
        viewPager.setAdapter(
                new MyFragmentStatePagerAdapter(getSupportFragmentManager(), this));
        
        ImageView next_button = (ImageView)findViewById(R.id.goto_next);
        
        //「次へ」ボタンをタップすると、次のページへ進む処理
        next_button.setOnTouchListener(new OnTouchListener() {
            @Override
            public boolean onTouch(View arg0, MotionEvent arg1) {
                switch (arg1.getAction()) {
                case MotionEvent.ACTION_DOWN: {
                    
                        int current_page_num = viewPager.getCurrentItem();
    
                        viewPager.setCurrentItem(current_page_num + 1);
                        
                        break;
 
                    }
                
                }
                return true;
            }
        });
 
        //最後のページだけ、「次へ」ボタンを表示しない処理
        viewPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
            @Override
            public void onPageSelected(int position) {
                    
                ImageView next_button = (ImageView)findViewById(R.id.goto_next);                    
                
                if(position==3){
                        
                    next_button.setVisibility(View.INVISIBLE);
                        
                }else{
                    
                    next_button.setVisibility(View.VISIBLE);
                    
                }
            }
        });
        
       skip = (Button) findViewById(R.id.skip);
       skip.setOnClickListener(new OnClickListener(){
 
            @Override
            public void onClick(View v) {
                if (v == skip ) {
                    //スキップをタップした時の処理
                }
                
            }
            
        });
        
        
    }
 //アダプター
 //MyFragmentStatePagerAdapter.java内
 
 public class MyFragmentStatePagerAdapter
        extends FragmentStatePagerAdapter {
 
    private Context localContext;
 
    public MyFragmentStatePagerAdapter(FragmentManager fm, Context context) {
        super(fm);
        localContext = context;
    }
 
    @Override
    public Fragment getItem(int i) {
 
        switch (i) {
            case 0:
                return new TutorialCreditFragment();
            case 1:
                return new TutorialGroupFragment();
            case 2:
                return new TutorialJoinFragment();
            case 3:
                return new TutorialAdminFragment();
            default:
                return new TutorialCreditFragment();
        }
 
    }
 
    @Override
    public int getCount() {
        return 4;
    }
 
    @Override
    public CharSequence getPageTitle(int position) {
        // return "Page " + position;
 
        switch (position) {
            case 0:
                return localContext.getString(R.string.credit_explanation_title);
            case 1:
                return localContext.getString(R.string.group_explanation_title);
            case 2:
                return localContext.getString(R.string.join_explanation_title);
            case 3:
                return localContext.getString(R.string.admin_explanation_title);
            default:
                return localContext.getString(R.string.tutorial_before);
        }
 
    }
 
 }
 //個別のフラグメント
 //TutorialCreditFragment.java 内
 //他のフラグメントも同様な感じなので、省略します。
 
 public class TutorialCreditFragment extends Fragment {
 
    @Override
    public View onCreateView(LayoutInflater inflater,
      ViewGroup container,
      Bundle savedInstanceState) {
      View v = inflater.inflate(R.layout.tutorial_credit, null);
      
      return v;
    }
 
 }
 //レイアウトファイル tutorial_before_start_fragment.xml内
 //上にタイトルバーがあり、下に次へボタンとSkipボタンがある構成
 
  <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >
 
    <!-- ページャーのホルダー 個々のページは、フラグメント内 -->
 
    <android.support.v4.view.ViewPager
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/pager"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >
 
        <android.support.v4.view.PagerTitleStrip
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="top"
            android:background="#e38e66"
            android:padding="10dp"
            android:textColor="@color/white"
            android:textSize="14sp" />
    </android.support.v4.view.ViewPager>
 
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:orientation="vertical" >
 
        <ImageView
            android:id="@+id/goto_next"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:layout_margin="5dp"
            android:contentDescription="@string/swipe_left"
            android:src="@drawable/goto_next" />
        
        <Button
            android:id="@+id/skip"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="5dp"
            android:background="#aaa6a4"
            android:gravity="bottom|left"
            android:padding="5dp"
            android:text="@string/skip"
            android:textColor="@color/white"
            android:textSize="12sp" />
        
     </LinearLayout>
 
 </RelativeLayout>
 //個々のフラグメントのレイアウトファイル
 //tutorial_credit.xml内
 //他のフラグメントも同様なので、省略 
 
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:background="#f9eee9"
    android:gravity="center_horizontal"
    android:orientation="vertical"
    android:padding="10dp"
    android:textColor="#000000" >
 
 
        <LinearLayout
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:gravity="center_horizontal"
            android:orientation="vertical" >
 
            <TextView
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:text="@string/step1"
                android:textColor="#c04208"
                android:textSize="14sp" />
 
            <ImageView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginBottom="10dp"
                android:contentDescription="@string/credit_image"
                android:gravity="center_horizontal"
                android:src="@drawable/registration_explanation_credit" />
 
            <TextView
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:layout_marginBottom="10dp"
                android:text="@string/credit_explanation_text"
                android:textColor="@android:color/black"
                android:textSize="14sp" />
 
        </LinearLayout>
 
 </LinearLayout>

Android Fatal signal 11 SIGSEGV

Android

Android Fatal signal 11 SIGSEGV というエラーが出て強制終了する

昨今Androidで動画を撮るというプロジェクトにまい進している私ですが(Android ビデオを録画するサンプルコード)、謎のエラーで強制終了してしまうというエラーにかなりはまってしまいました。

 Fatal signal 11 (SIGSEGV) at 0x00000010 (code=1), thread 30664

というエラーが出て、いきなり落ちちゃうんですねー。
使っていた端末は、Galaxys3aです。

SIGSEGV(しぐせぐぶ??)とは、セグメンテーション違反の際におこるエラーで、Wikipediaさんに詳しいです。
セグメンテーション違反

StackOverflowさんにもいくつか記事がありますが、Javaが吐くエラーではないので、ファームウェアとかの問題だ、という話もあります。。。

http://stackoverflow.com/questions/11274341/android-sigsegv-error-when-recording-audio

「どうすればええのんや…」

と悩み、まずは色々とtry~chatchでエラー箇所を捕捉しようと考えましたが、セグメンテーション違反でJavaのエラーではないので、try~catchでは捕捉できませんでした。

デバッグをしてみたところ、落ちるタイミングを発見。

 mrec = new MediaRecorder();
 
 //中略
 
 mrec.stop(); //このstopでSIGSEGVエラー
 mrec.reset();
 mrec.release();

どうやら既にストップしていたmrecをstop()させていたことが原因のようです。

フラグを作って、stopしたかどうかを判断し、一度ストップしたmrecをもうstopさせないようにしたら治りました!!

Android Espresso 初心者導入用のサンプル

Android

Android JUnit4 初心者導入用のサンプル]]というのを以前書きましたが、今回は、[[Espressoというフレームワークを使ったJUnitのテストを作ります。
前置きですが、自分自身初心者なので、「初心者用のサンプル」とはいささかおこがましいですが(^_^)、何か間違いがあれば、訂正していってください┌o ペコッ

まずは、gradle。
いっつもねー ここで必ず一波乱ある…。
もれなく今回も。
今回の問題は、該当のアプリのtargetSdkのバージョンを、22にしていたことです。
どうしても、22のままでは、espressoを読み込めなかった…orz
なので、targetSdkのバージョンを、23にしました。

下記のサイトのほぼコピペですが、appのレベルのbuild.gradleを次のように編集します。

https://developer.android.com/training/testing/start/index.html?hl=ja#config-instrumented-tests

 dependencies {
    androidTestCompile 'com.android.support:support-annotations:23.0.1'
    androidTestCompile 'com.android.support.test:runner:0.4.1'
    androidTestCompile 'com.android.support.test:rules:0.4.1'
    // Optional -- Hamcrest library
    androidTestCompile 'org.hamcrest:hamcrest-library:1.3'
    // Optional -- UI testing with Espresso
    androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.1'
    // Optional -- UI testing with UI Automator
    androidTestCompile 'com.android.support.test.uiautomator:uiautomator-v18:2.1.1'
 }
 android {
    defaultConfig {
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
 }

んで、次の問題。

 @RunWith(AndroidJUnit4.class)

 Cannot resolve symbol 'AndroidJUnit4'

で読み込めない。

もちろん、espressoさんも

 import static android.support.test.espresso.Espresso.onView;

 Cannot resolve symbol 'espresso'

で読み込めない。。。

これに結構はまりましたが、正解は、テスト用のコードの置き場所が違っていたのでした…。

app\src\testの下ではなく、app\src\androidTestの下にテストコードを置かないとダメなのです。

espresso.jpg

このあたり、どっかの設定とかで変更できるのかなー

よくわかりません。

で、やーっとテストできるかと思いきや!
上記の図にある、RegisterActivityTest.javaを右クリックして、Run RegisterActivityTestを押しても、

 Class not found:  "jp.onlineconsultant.hogehoge.RegisterActivityTest"Empty test suite.

となってしまい、テストできません。(つД`)

これも、jp.onlineconsultant.hogehogeのディレクトリのところを右クリックして、Run Tests in と書いてあるのをクリックすると、テストができます。

さあてさて、RegisterActivityTest.javaはこんな感じです。
初心者用の超シンプルなサンプルですので、ご容赦を。

 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class RegisterActivityTest {
 
    private RegisterActivity mRegisterActivity = null;
    private Button mRegisterButton;
    private EditText mLast_name_box;
 
    @Rule
    public ActivityTestRule<RegisterActivity> mActivityRule = new ActivityTestRule<>(
            RegisterActivity.class);
 
    @Before
    public void setUp(){
 
        onView(withId(R.id.register_button)).check(matches(withText("送信")));
 
    }
 
    //テストが動作するかだけの確認用
    @Test
    public void testGetCrowd() {
        Assert.assertEquals(1, 1);
    }
 }