備忘録 〜プログラミング〜

プログラミングに関する事をつらつらと、、

NetworkImageView使用時のreload方法

NetworkImageViewを使用しているときに、通信エラーなどで画像が表示されなかった場合、Error画像を出すように実装していたのですが、リロードをしても、Error画像が消えずに残ってしまう現象が起きました。
setImageUrl(url, mImageLoader)メソッドを呼ぶだけで大丈夫なのかと思っていたのですが、実際はその前に

setImageUrll(null, mImageLoader);
setImageUrl(url, mImageLoader)

Cacheを消すなど、色々と試してみたのですが、上記の方法で無事リロードした際に画像がうまく読み込まれるようになりました。
他に方法もありそうですが、一旦これで解決です。

Androidのスクリーンショットを撮るシェルスクリプト

キャプチャーを撮るのはAndroidStudioでも簡単に出来るのですが、シェルスクリプトでキャプチャーを撮るほうが楽な場合もあったのでメモ。

#!/bin/bash
DATE=`date +"%Y-%m-%d-%H-%M-%S"`
FILENAME="s-${DATE}.png"
echo "capturing ${FILENAME}..."
adb shell screencap -p "/sdcard/${FILENAME}"
adb pull /sdcard/"${FILENAME}"
adb shell rm "/sdcard/${FILENAME}"
echo "saved ${FILENAME}."

capture.shなどでデスクトップに保存。
パソコンとAndroid端末を繋いで、スクリーンショットを撮りたい画面を表示。
ターミナルから以下コマンドでスクリーンショットが撮れます。

$ cd ~/Desktop
$ ./capture.sh

adb logcatでint logctl_get(): open '/dev/hwlog_switch' fail -1, 13. Permission deniedが出る問題

HUAWEI MT7-J1でログを出力しようと思ったときにlogctl_get()がPermission deniedで出力が出来ない問題が発生しました。
問題解決は簡単で、
1:電話を起動
2:[ *#*#2846579#*#* ]をダイアル
3:メニューが出てくるので、"ProjectMenu" -> "Background Setting" -> "Log Setting"の順に進む
4:Log switchの項目を選択してONにする
5:再起動
これでオッケーです

stackoverflow.com

AndroidでMapをソートしたくなったので作成したクラス

Listのソートは分かったのですが、Mapクラスのソートがしたくなったので、stackoverflowを参考に作成しました。
そのときのメモ

import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

public class MapUtil {
    public static <K, V extends Comparable<? super V>>Map<K, V> sortByKey(Map<K, V> map){
        List<Map.Entry<K, V>> list = new LinkedList<Map.Entry<K, V>>( map.entrySet() );
        Collections.sort(list, new Comparator<Map.Entry<K, V>>() {
            @Override
            public int compare(Map.Entry<K, V> lhs, Map.Entry<K, V> rhs) {
                return (Integer.valueOf((String)rhs.getKey())).compareTo( Integer.valueOf((String) lhs.getKey()) );
            }
        });
        Map<K, V>result = new LinkedHashMap<K, V>();
        for(Map.Entry<K, V>entry: list){
            result.put(entry.getKey(), entry.getValue());
        }
        return result;
    }
    public static <K, V extends Comparable<? super V>>Map<K, V> sortByValue(Map<K, V> map){
        List<Map.Entry<K, V>> list = new LinkedList<Map.Entry<K, V>>( map.entrySet() );
        Collections.sort(list, new Comparator<Map.Entry<K, V>>() {
            @Override
            public int compare(Map.Entry<K, V> lhs, Map.Entry<K, V> rhs) {
                return (lhs.getValue()).compareTo( rhs.getValue() );
            }
        });
        Map<K, V>result = new LinkedHashMap<K, V>();
        for(Map.Entry<K, V>entry: list){
            result.put(entry.getKey(), entry.getValue());
        }
        return result;
    }
}

他にも方法はあると思いますが、このクラスを使用する事でMapクラスのソートは出来るようになりました。stackoverflow.com

ListViewと、ScrollView使用時にスクロールの移動距離を取得する方法

下にスクロール時にヘッダーメニューを隠して、上にスクロール時にヘッダーメニューを表示する機能を実装する必要があったのですが、ListViewで移動距離を取得する方法が分からなくて調べてみました。
ScrollViewのほうも作ってみたので、まずはScrollViewのほうから、
【ScrollViewを継承したクラスを作成する】

public class ObservableScrollView extends ScrollView{
    public interface ScrollViewListener {
        void onScrollChanged(int x, int y, int oldX, int oldY);
    }
    private ScrollViewListener scrollViewListener = null;
    public ObservableScrollView(Context context) {
        super(context);
    }
    public ObservableScrollView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
    public ObservableScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }
    public void setOnScrollViewListener(ScrollViewListener scrollViewListener) {
        this.scrollViewListener = scrollViewListener;
    }
    // onScrollChanged が複数回呼び出されるのを防止するために利用
    private int lastt = 0;
    @Override
    protected void onScrollChanged(int x, int y, int oldX, int oldY) {
        super.onScrollChanged(x, y, oldx, oldy);
        // 高速にスクロールした場合、画面上部で t, oldt がマイナスとなる場合は処理をスキップ
        if (scrollViewListener == null || lastt == y || y < 0 || oldy < 0) {
            return;
        }
        lastt = y;
        scrollViewListener.onScrollChanged(x, y, oldX, oldY);
    }
}

【使用方法】

final ObservableScrollView obScrollView = (ObservableScrollView) view.findViewById(R.id.contents_list_scroll_view);
  obScrollView.setOnScrollViewListener(new ObservableScrollView.ScrollViewListener() {
    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    @Override
    public void onScrollChanged(int x, int y, int oldX, int oldY) {
      int diff = y - oldY; // 縦軸の移動量
      //以下は、スクロールでヘッダーを表示、非表示にする処理
      int translationY = 0 < diff
        ? Math.max((int) mHeader.getY() - diff, -mHeader.getHeight())
        : Math.min((int) mHeader.getY() - diff, 0);
      mHeader.setTranslationY(translationY);
    }
  });

続いて、ListView、
【移動距離を計算するヘルパークラスを用意する】

public class ListViewScrollTracker {
    private AbsListView mListView;
    private SparseArray<Integer> mPositions;
    public ListViewScrollTracker(final AbsListView listView){
        mListView = listView;
    }
    public int calculateIncrementalOffset(final int firstVisiblePosition, final int visibleItemCount){
        SparseArray<Integer> previousPositions = mPositions;
        mPositions = new SparseArray<Integer>();
        for(int i = 0; i < visibleItemCount; i++){
            mPositions.put(firstVisiblePosition + i, mListView.getChildAt(i).getTop());
        }
        if(previousPositions != null){
            for(int i = 0; i < previousPositions.size(); i++){
                int position = previousPositions.keyAt(i);
                int previousTop = previousPositions.get(position);
                Integer newTop = mPositions.get(position);
                if(newTop != null){
                    return newTop - previousTop;
                }
            }
        }
        return 0;
    }
    public void clear(){
        mPositions = null;
    }
}

【使用方法】

//スクロール値の相対置を調べるために使用
private ListViewScrollTracker mScrollTracker;
//ListViewの相対的なY位置
private int mOffsetY;
//ジャンルから探す/並び替えをスクロールに応じて表示、非表示にするために使用
private int mOldOffsetY;
private ListView mListView;
private LinearLayout mHeader;

@Override
protected void onCreate(Bundle savedInstanceState) {
  //使用するListView
  mListView = (ListView)view.findViewById(R.id.contents_list);
  mHeader = (LinearLayout)view.findViewById(R.id. store_top_header);
  mScrollTracker = new ListViewScrollTracker(mListView);
  mListView.setOnScrollListener(new AbsListView.OnScrollListener() {  
    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    @Override
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
      mOffsetY += mScrollTracker.calculateIncrementalOffset(firstVisibleItem, visibleItemCount);
      int diff = mOldOffsetY - mOffsetY; // 縦軸の移動量      
      //以下は、スクロールでヘッダーを表示、非表示にする処理
      int translationY = 0 < diff
        ? Math.max((int) mHeader.getY() - diff, -mHeader.getHeight())
        : Math.min((int) mHeader.getY() - diff, 0);
      mHeader.setTranslationY(translationY);
    }
      mOldOffsetY = mOffsetY;
    }
  });
}

簡単にですが、レイアウトのxmlのサンプルも

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_height="match_parent"
  android:layout_width="match_parent"
  android:orientation="vertical">
  <ListView
        android:id="@+id/contents_list"
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
  </ListView>
  <LinearLayout
    android:id="@+id/store_top_header"
    android:layout_width="match_parent"
    android:orientation="vertical"
    android:layout_height="wrap_content">
  </LineaLayout>
</FrameLayout>

長くなりましたが、このような感じでスクロールの移動距離を取得する事が出来ましたー。

ViewPagerを使用するときにwrap_contentを適応する方法

ViewPagerを使用しているとき、高さを画像に合わせようと思ったのですが、実際に使用してみると画像が小さくなってしまいます。
なので、高さを(120dp)などとして固定で使用していたのですが、やはりwrap_contentを使いたい。という事で調べてみたところ、
ViewPagerを継承したクラスを作成して、そのクラスを使用する事で可能という事が分かりました。
その継承クラスは、こちら。

public class MyViewPager extends ViewPager{
  public MyViewPager(Context context){
    super(context);
  }

  public MyViewPager(Context context, AttributeSet attrs){
    super(context, attrs);
  }

  @Override
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
    int height = 0;
    for(int i=0; i<getChildCount(); i++){
      View child = getChildAt(i);
      child.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
      int h = child.getMeasuredHeight();
      if(h > height) height = h;
    }
    heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
  }
}

これで、何とか成功しましたー。stackoverflow.com

コード上で高さを数値で設定するときに、デバイス毎に高さが変わってしまう問題

コード上で動的に高さを変更する必要が出てきたのですが、以下のように設定しても、デバイス毎に高さが変わってしまいます。

final ScrollView scrollView = (ScrollView) view.findViewById(R.id.scroll_view);
RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
lp.setMargins(0, 60, 0, 0);
scrollView.setLayoutParams(lp);

そもそもこのsetMarginsで設定している、60のサイズはpxなのか、dpなのかどちらなのかを調べたところ、コード上で設定する値は、pxだという事が分かりました。stackoverflow.com
なので、このpxをdpに変換するメソッドを作成

//pxをdpに置換
private static int convertPxToDp(Context context, int px){
  float d = context.getResources().getDisplayMetrics().density;
  return (int)((px / d) + 0.5);
}

//dpをpxに置換
public static int convertDpToPx(Context context, int dp){
  float d = context.getResources().getDisplayMetrics().density;
  return (int)((dp * d) + 0.5);
}

これを使用。

final ScrollView scrollView = (ScrollView) view.findViewById(R.id.scroll_view);
RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
lp.setMargins(0, convertDpToPx(60), 0, 0);
scrollView.setLayoutParams(lp);

これで、端末毎の高さが変わる事はないかなと、、stackoverflow.com