【android】Fragment上のListViewに置かれたボタンのonClickを処理する

はじめに

今回やりたかったのは、ListViewのセル上に削除ボタンとかをおいてクリックされたらListViewから削除するようなこと。
ListViewを作るのがActivityとFragmentとでやり方が違うみたい。

Activityの場合は以下みたいな形でボタンのイベントが取得できる。

xml

<Button ・・・
  android:id="@+id/myButton"
  android:onClick="myFunc"
 />

Adapter

public View getView(final int position, View convertView, ViewGroup parent) {
    ・・・
    Button myButton = (Button)convertView.findViewById(R.id.myButton);
    myButton.setTag(position);
    ・・・
}

Activity

public void myFunc(View view) {
    // ListView上のボタンがおされたら呼ばれる
    // view.getTag()でどの行が押されたかわかる
}

けど、Fragmentを使ってる場合はFragmentを呼び出してるActivityで実装されているmyFuncが呼ばれるためやりたいこととは違った。

Fragmentの場合

xmlのButtonにonClick属性はセットしない。
やり方わからなかったので、アダプターにインターフェースを定義して呼び出し元(Fragment)にメソッドを実装させる形で実装してみた。

xml

<Button ・・・
  android:id="@+id/myButton"
 />

Adapter

public class MyAdapter extends ArrayAdapter<MyData> {

    private MyAdapterListener listener;

    public interface MyAdapterListener {
        void myFunc(Object position);
    }

    public void setListener(MyAdapterListener listener) {
        this.listener = listener;
    }

    ・・・

    @Override
    public View getView(final int position, View convertView, ViewGroup parent) {
        ・・・
        Button myButton = (Button)convertView.findViewById(R.id.myButton);
        myButton.setTag(position);
        myButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                listener.myFunc(v.getTag());
            }
        });
        return convertView;
    }
}

Fragment

public class MyFragment extends Fragment implements MyAdapter.MyAdapterListener {
    ・・・
    @Override
    public void onCreate(Bundle savedInstanceState) {
        ・・・

        // アダプターで用意したsetListnerメソッドで関連づける
        mAdapter = new MyAdapter(getContext(), 0, ・・・);
        mAdapter.setListener(this);
    }

    public void myFunc(Object position) {
        // 呼ばれた!!
    }
}

もっといいやり方ありそうだけど。。以上です

【android】ListViewひな型

試したりするときにとりあえずListView作ることが多いのでコピペ用にメモ

MainActivity.java

public class MainActivity extends AppCompatActivity {
    private ArrayList<Item> mItems;
    private ItemAdapter mAdapter;
    private ListView mListView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mItems = new ArrayList<>();
        mItems.clear();
        mAdapter = new ItemAdapter(this, 0, mItems);

        ListView listView = (ListView)findViewById(R.id.list_view);
        listView.setAdapter(mAdapter);

        setData();
    }

    private void setData() {
        for (int i = 0; i < 10; i++) {
            Item item = new Item();
            item.setTitle("タイトル " + String.valueOf(i+1));
            mItems.add(item);
        }
    }
}

class Item {
    private String title;

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }
}

class ItemAdapter extends ArrayAdapter<Item> {
    private LayoutInflater layoutInflater;

    public ItemAdapter(Context c, int id, ArrayList<Item> data) {
        super(c, id, data);
        this.layoutInflater = (LayoutInflater)c.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    }

    @Override
    public View getView(final int position, View convertView, ViewGroup parent) {
        if (convertView == null) {
            convertView = layoutInflater.inflate(R.layout.list_item,parent,false);
        }
        Item data = getItem(position);

        TextView title = (TextView)convertView.findViewById(R.id.title);
        title.setText(data.getTitle());

        return convertView;
    }
}

main_activity.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:id="@+id/activity_main"
                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">

  <ListView
      android:id="@+id/list_view"
      android:layout_width="match_parent"
      android:layout_height="match_parent"></ListView>
</RelativeLayout>

list_item.xml

<?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:id="@+id/title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:paddingLeft="5sp"
        android:paddingTop="25sp"
        android:paddingBottom="15sp"
        android:textSize="24sp"/>
</LinearLayout>                                                                                                                                                                                                    
||<                                                                                                                                                                                                                

【android】ViewPagerでタブ毎に戻れる画面を実装する方法

はじめに

今回やりたかったのはこんな画面。
f:id:yoppy0066:20170131000025g:plain:w250

なんでもiOSと比較するのはあれだけど、iOSだとUiNavigationControllerを持つUITabbarControllerみたいなイメージ。
今回はタブは表示していないけどViewPagerをタブ表示するサンプルはたくさんあるので問題なかった。
戻るボタンでの戻り先をタブごとに保持しているようなイメージ。

クラス構成としてはこんなイメージ。
Activityは1つでタブごとにRootFragmentを持たせて、実際に画面に表示するフラグメントを操作するイメージ(ここではFirstFragmentとSecondFragment)。

f:id:yoppy0066:20170131000304p:plain

実装

MainActivityの実装

ここでやることはViewPagerとRootFragmentを紐づける処理。
あとは戻るボタンされた時に選択中のタブのバックスタックから1つ取り出して戻す処理。
戻るボタンされたときに選択中のタブがわかるようにタブ選択されるたびに選択中のタブ番号を保持する。

MainActivity.java

public class MainActivity extends AppCompatActivity {

    private ViewPager mPager;
    private String[] mTitles = {"タブ1", "タブ2"};

    // 選択中のタブ番号を保持
    private int selected;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
	setContentView(R.layout.activity_main);
        setTitle("タブ1");
	mPager = (ViewPager)findViewById(R.id.viewpager);

        mPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
            @Override
            public void onPageSelected(int position) {
		selected = position;
		setTitle(mTitles[position]);
            }
            ・・・
        });

        FragmentPagerAdapter adapter = new FragmentPagerAdapter(getSupportFragmentManager()) {
            @Override
            public Fragment getItem(int position) {
                return RootFragment.newInstance(mTags[position]);
            }

            @Override
            public int getCount() {
                return 2;
            }
            ・・・
        };
        mPager.setAdapter(adapter);
    }

    // 戻るボタン
    @Override
    public void onBackPressed() {

        // 選択中のタブのRootFragmentにバックスタックがあれば戻る処理
        FragmentManager fm = getSupportFragmentManager();
        List<Fragment> fragments = fm.getFragments();
        Fragment fragment = fragments.get(selected);
        FragmentManager fragmentManager = fragment.getChildFragmentManager();
        if (0 < fragmentManager.getBackStackEntryCount()) {
            fragmentManager.popBackStack();
        }
    }
}

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:id="@+id/activity_main"
                ・・・
                />

  <android.support.v4.view.ViewPager
      android:id="@+id/viewpager"
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      android:layout_weight="1"
      android:background="@android:color/white"
      />

</RelativeLayout>
RootFragmentの実装

ここではFirstFragmentを表示するだけ。それならRootFragmentなんて不要と思って最初実装していた。
けど、ViewPagerの中のFragmentをreplaceしても中身がレイアウトの表示が変わらない問題に直面。で

http://www.pineappslab.com/post/fragments-viewpager/
ここの記事を発見。ここの通りにやらせていただきました。

RootFragment.java

public class RootFragment extends Fragment {

    public static RootFragment newInstance() {
        RootFragment fragment = new RootFragment();
        return fragment;
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_root, container, false);
        FragmentTransaction fragmentTransaction = getChildFragmentManager().beginTransaction();
        fragmentTransaction.replace(R.id.root_frame, FirstFragment.newInstance());
        fragmentTransaction.commit();
        return view;
    }
}

fragment_root.xml

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/root_frame"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
</FrameLayout>
FirstFragmentの実装

FirstFragment.java

public class FirstFragment extends Fragment {

    public static FirstFragment newInstance(String tag) {
        FirstFragment fragment = new FirstFragment();
            Bundle args = new Bundle();
            return fragment;
        }

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
            View view = inflater.inflate(R.layout.fragment_first, container, false);
            Button button = (Button) view.findViewById(R.id.button);
            button.setOnClickListener(new View.OnClickListener(){

            @Override
            public void onClick(View v) {
                FragmentTransaction fragmentTransaction = getParentFragment().getChildFragmentManager().beginTransaction();
                fragmentTransaction.replace(R.id.root_frame, SecondFragment.newInstance());
                fragmentTransaction.addToBackStack();
                fragmentTransaction.commit();
            }
        });
         return view;
    }
}

fragment_first.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    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:layout_marginBottom="10sp"
      android:text="FirstFragment"
      android:textSize="30sp"/>

  <Button android:id="@+id/button"
          android:layout_width="fill_parent"
          android:layout_height="wrap_content"
          android:background="#ffff00"
          android:text="次へ"
          android:textSize="18sp"/>

</LinearLayout>

SecondFragmentは画面表示するだけだからいいかな。。。

おわりに

よくわからないで調べながらやってたからこの画面実装するポイントは他にもけっこうあるはずなんだけど出てこない。。。
もうちょっと内容を分割して整理した方が良さそう。以上です

はてなブログ(はてな記法)で画像を横並びにする方法メモ

なにげに時間かかったのでメモしておく。
ちなみに自分は、はてな記法モードっていうのを使ってる。

手順は以下だった
・アップロードした画像のURLを取得
・tableタグとimgタグ使って整形


やりたかった画面




アップロードした画像のURLを取得

アップロードした画像ははてなフォトにあるのでここから取得できる。

1. マイフォトを開く
f:id:yoppy0066:20170127112543p:plain

2.Hatena Blogフォルダを開く
f:id:yoppy0066:20170127112552p:plain

3. フォルダ編集を開く
f:id:yoppy0066:20170127112601p:plain

4. 画像を選択してブログを貼り付けるをクリックするとurlを取得できる
f:id:yoppy0066:20170127112607p:plain

tableタグとimgタグ使って整形

あとはtableタグとimgでスタイルシートも使えるのでこんなかんじで。

<table style="border-style: none;" border="1"><tr>
  <td style="border-style: none;">
    <img src="画像のURL" height="150">
  </td>
  <td style="border-style: none;">
    <img src="画像のURL" height="150" />
  </td>
</table>

以上です

【css】imgやvideoをアスペクト比を保ちつつ画面一杯に表示する

はじめに

機会ないかもしれないけど次やるときのためのメモ。
やりたいのはスマホでこんな感じの画面。画像でも動画でも同じcssで。

横画像


portrait
20170127094740

landscape

縦画像


portrait
20170127094930

landscape
20170127094930

実装

htmlはどちらの動画も共通で、cssは横長と縦長とでそれぞれ用意する。

html

<div id="wrapper">
  <img src="/path/to/file" />
</div>

320x180(横長)のcss

body {
  margin:0;
}

#wrapper {
  position:relative;
  width: 100%;
  height:100%;
  background-color:black;
}

#wrapper > img {
  width: 100%;
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  margin: auto;
}

400x625(縦長)のcss

body {
  margin:0;
}

#wrapper {
  height: 100%;
  text-align:center;
  background-color:black;
}

#wrapper > * {
  height: 100%;
}

以上です

【android】apiへリクエストしてcallbackを実装する方法メモ

はじめに

android調べながらやってるけどJavaの基礎もあんまないから時間かかる。
今回やりたいのはapiたたいてレスポンスによって成功用と失敗用のcallbackを実行するようなこと。
調べたり質問しながらやってるけどinterface使うといいらしい。

実装1

呼び出し側に成功時と失敗時のコールバック用インターフェスを実装させる。
すっきりしてる気もするけど、コールバックを必ず実装させなければならないのがちょっと嫌かも。

ApiManager.java(呼ばれる側)

public class ApiManager {
    public interface ApiManagerCallback {
        public void success(Object obj);
        public void failed(Object obj);
    }

    private ApiManagerCallback apiManagerCallback;

    public void setCallbacks(ApiManagerCallback apiManagerCallback) {
        this.apiManagerCallback = apiManagerCallback;
    }

    public void callApi() {
        try {
            apiManagerCallback.success("success");
        } catch (Exception e) {
            apiManagerCallback.failed("failed");
        }
    }
}

MainActivity.java(呼び出す側)

public class MainActivity extends AppCompatActivity implements ApiManager.ApiManagerCallback {
   @Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_main);

       ApiManager apiManager = new ApiManager();
       apiManager.setCallbacks(this);
       apiManager.callApi();
   }

   public void success(Object obj) {
       // 成功
   }

   public void failed(Object obj) {
       // 失敗
   }
}

実装2

実装1より呼び出す側のコードが少し見辛い気もするけど、失敗時のコールバックが必須でないのがいいかも。

ApiManager.java(呼ばれる側)

public class ApiManager {
    public interface ApiManagerCallback {
       void call(Object obj);
    }

    void connect(final ApiManagerCallback success, final ApiManagerCallback failed) {
        try {
            success.call("success!!");
        } catch (Exception e) {
            if (failed != null) {
                failed.call("failed");
            }
        }
    }
}

MainActivity.java(呼び出す側)

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        ApiManager apiManager = new ApiManager();
        apiManager.connect(new ApiManager.ApiManagerCallback() {
            @Override
            public void call(Object obj) {
                // 成功
            }
        }, new ApiManager.ApiManagerCallback() {
            @Override
            public void call(Object obj) {
                // 失敗
            }
        });
    }
}

きっともっといい書き方あるんだろうけど、自分の頭ではこんなものかな。難しい。。。もっといい書き方教えてください。以上です

石の上にも3年

初めて正社員として就職した会社を辞めると伝えたときに上司に言われた言葉。
何があっても3年は続けろって意味だと思うけど当時自分は1年半でその会社を辞めた。

その後あまり長続きすることもなく職場を転々としてきた気がする。適当な理由で退職したが本音は以下だったと記憶している。
1. 家庭の事情 〜1年半在籍(100人規模のSES事業)
2. 会社の新体制が嫌だった 〜5年在籍(70人規模の受託開発会社)
3. 社長が怖すぎた 〜3ヶ月在籍(3人規模の何か)
4. より技術を高められそうな環境で働きたかった 〜2年在籍(20人規模のCP)

今思うと自分の対応次第で辞める必要なかったと思えるものがいくつかある。
結局自分の言いたいことを言わずに勝手にイライラして何も言わずにやめるってパターンが1番もったいなかった気がする。
1と3は今考えても仕方ない。2と4は他にも選択肢があったと思う。

何も言わないことで丸く納めていたつもりでも、それがつもって辞めたら会社にとっては1番不利益。結局コミュニケーション能力ってそこなのかな。。。以上です