Vue.jsを使って、facebookの通知エリアみたいなものを作るサンプルコード

「facebookの通知エリアみたいなもの」と言われても漠然としていると思いますが表示でいうと次のようなものです。

文章で要件を書くと次の通りです。

  • アイコンの右肩or左肩に未読件数のバッジが付いている
  • アイコンにマウスオーバーで下にプルダウンで表示される
  • メッセージ内容は動的に変更できる
  • 一件一件選んで消せる
  • 消したものは、既読フラグをつけて、もう未読エリアに表示しない
  • 重要なメッセージは色を変更するなどでわかりやすくする

私の javascript に関する能力としては、Web関係の仕事を5.6年前までは結構一生懸命やっていたけれども、javascriptは結局必要に迫られたときにjQueryとか jQuery UIとかでなんとかしていて、そんなに深くはやっていない、という感じのスキルです。

今回は、ちょっと急ぎで上記のものを作らないといけなくって、

「はは!この秀吉が、一夜で城を建ててみまする!」

という感じで、サクッとやる感じでやりました。結局4日間ぐらいはかかりましたので、4日城ですね。

使っているのは

  • Vue.js
  • Vuetify.js(バッジのデザインをこれにしたかった)
  • Semantic UI (メッセージのデザインをこれにしたかった)
  • axios(閉じるボタンをクリックした時に何か実行するため)

びみょいところもありますが、載せちゃいます。

<!DOCTYPE html>
<html>
<head>
<link href="https://cdn.jsdelivr.net/npm/vuetify/dist/vuetify.min.css"
	rel="stylesheet">
<link rel="stylesheet"
	href="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.4.1/semantic.min.css">

</head>
<body>
	<br>
	<div id="app">
		<div class="ui button"
			style="background-color: #ffffff; margin-left: 100px;">
			<!-- メールのアイコン -->
			<v-app style="height:50px; background-color:#ffffff;"> 
				<v-layout /> 
					<a>
						<v-badge color="red"  left> 
							<template v-slot:badge> 
								<span v-if="messages > 0">{{ messages }}</span>
							</template> 
							<bell v-on:showmenu="showMenuBelowBell()"
							v-on:hidemenu="hideMenuBelowBell()">
							</bell>
						</v-badge>					
					</a>
				</v-layout>
			</v-app>

			<!-- 通知 -->
			<notification v-for="post in posts" v-bind:key="post.id"
				v-bind:title="post.title" v-bind:content="post.content"
				v-bind:type="post.type" v-if="show_messages"></notification>
		</div>
	</div>

	<!-- 通知部分のテンプレート -->
	<script type="text/x-template" id="notification-component">
	  <div class="ui message" :class="type" v-if="!hidden" style="width:300px; text-align:left">	  
		<i class="close icon" @click="hide"></i>
		<div class="header">{{ title }}</div>
		<slot></slot>
		<div v-html="content"></div>
   	  </div>
	</script>

	<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
	<script src="https://cdn.jsdelivr.net/npm/vuetify/dist/vuetify.js"></script>
	<script src="https://unpkg.com/axios/dist/axios.min.js"></script>

	<script>

	  Vue.component('bell', ({
			  template: '<span><i class="bell icon" v-on:mouseover="mouseover" ></i></span>',
	     	  methods: {
	  		    mouseover: function(){
	  		      console.log("マウスオーバーだよ");
		  		  this.$emit('showmenu') 
	  		    },
	
	  		    mouseleave:function(){
	  			  console.log("マウスリーブだよ");
	  			  this.$emit('hidemenu')
	  			},
	  		  }
		  	      
		}))	
			
  
	  Vue.component('notification', {
		  props: {
			  		title:{}, 
			  	  	content:{},
			        type: {
			            type: String,
			            default: 'info'
			         },
			        header: {
			       }
		  },
	      data() {
		        return {
		          hidden: false,
		   		 
		        }
		  },
	      methods: {
	          hide() {
	            this.hidden = true
	            axios.get(`https://api.github.com/users/AkikoGoto`)
	            .then(response => {
	              console.log(response.data)
	            })
	          },
	
	      },
		  template: '#notification-component'
		})	
		
		
		
	    
	  new Vue({
		  el: '#app',
		  vuetify: new Vuetify(),
		  data () {
		    return {
		     // show: false,
	
	 	      posts: [
	  		      { id: 1, title: 'せやかて' , content:'工藤', type:'info'},
	  		      { id: 2, title: 'なんだと' , content:'服部!本当なのか?', type:'error'},
	  		      { id: 3, title: 'お知らせ' , content:'ついに黒の組織のボスが判明'}
	  		    ],
	
	  		 messages: 3,
	  		 show_messages:false,
	
		    }
		  },
	
		  methods: {
	
			  showMenuBelowBell: function () {
			      this.show_messages = true
	
			    },
	
			  hideMenuBelowBell: function () {
				  this.show_messages = false
	
			    }		    		    
			    
			  }
	
		})
	
  </script>
</body>
</html>

一応、これを張り付けてhtmlファイルとしてブラウザで開けば、一番最初の画像のような挙動になるはずです。

注意点は次の通り。

  • データはVueを生成しているところ(new Vueの箇所)のpostsというところに入っています。JSONだとかなんらかの形で、ここが動的に変わるようにしてください。
  • バッジの件数は、その下のmessagesというオブジェクトになります。すみませんが、上記のpostsを自動的にカウントして入れるようになっていませんので、ここも別途やってくださいませ。
  • 閉じる×ボタンをクリックした時の挙動は、私のGithubにつながって応答が返ってきてconsoleに出力されるようになってます…。もちろん、ここも皆様がカスタマイズされると思いますので、ただのサンプルです。
  • プルダウンメニューが表示されるところはやってありますが、全体的に隠す処理はしていません。そちらもご自由に…。

Android エミュレーターで通信速度の遅い端末を作る Android Studio3.3.2

次の手順で行います。

Tools→

AVD Manager→

各エミュレーターの一覧出てくると思いますが、その鉛筆みたいなアイコンをクリック→

「Show Advanced Settings」をクリック

Netoworkという項目にSpeedとLatencyというのがありますので、それを変更。

・Fullがデフォルトで、PC上でできるかぎりのネットワーク速度を実現

・LTE なんか懐かしい  略称はLong-Term Evolution  らしい。

・HSDPA 多分、昔ハイスピードパケット通信とか言ってたあたりだと思うんですよね。この辺りから、遅さを体感できる…。

・GPRS 3Gぐらいの通信速度

下記の公式情報も参考にしてください。

https://developer.android.com/studio/run/managing-avds?utm_source=android-studio

java.lang.OutOfMemoryError: Failed to allocate a 840012 byte allocation with 555120 free bytes and 538KB until OOM

マイナーな話にはなっちゃうんですが。
AndroidのMapboxでマーカーを追加していて、タブレットで200個ぐらいマーカーを生成する画面でOOMが起こってクラッシュしてしまう、という不具合に遭遇しました。

ググると大きなBitmapをいっぱい追加するとこれが起きやすいという話です。

https://stackoverflow.com/questions/477572/strange-out-of-memory-issue-while-loading-an-image-to-a-bitmap-object

Android Developersの公式でも紹介されていますね。

https://developer.android.com/topic/performance/graphics

皆さん、大体ファイルを読み込んでアプリに表示する系でこれが発生しているようですね。

私はBitmapの生成を、Mapboxのマーカーをタップした時に出るウィンドウのために作ってまして、下記のような感じになります。

public static Bitmap generate(@NonNull View view) {
int measureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
view.measure(measureSpec, measureSpec);

int measuredWidth = view.getMeasuredWidth();
int measuredHeight = view.getMeasuredHeight();

view.layout(0, 0, measuredWidth, measuredHeight);
/Bitmap bitmap = Bitmap.createBitmap(measuredWidth, measuredHeight, Bitmap.Config.ARGB_8888);

//bitmap.eraseColor(Color.TRANSPARENT);
Canvas canvas = new Canvas(bitmap);
view.draw(canvas);
return bitmap;
}

https://docs.mapbox.com/android/maps/examples/symbol-layer-info-window/

コード自体はMapboxの公式サンプルを利用しているだけです。

なので、上述のAndroid DevelopersやStackOverflowにあるような

BitmapFactory.Options

を使ったやり方がどうにもできませんでした…。

なので、

Bitmap bitmap = Bitmap.createBitmap(measuredWidth, measuredHeight, Bitmap.Config.ARGB_8888);

Bitmap bitmap = Bitmap.createBitmap(measuredWidth, measuredHeight, Bitmap.Config.RGB_565);

に変更しました。

ARGB_8888 

にすると、1ピクセルにつき、4バイト使うらしいです。


RGB_565

にすると、1ピクセルにつき、2バイト使います。

透明部分とかが表現できませんが、しょうがない…。

ちなみに、手っ取り早くマニフェストファイルに

<application android:largeHeap=”true” > </appplication>

ってして回避することもできるようですが、これは機種によってできなかったりするらしく、また敗北感あるのでやめました…。

Tensorのshapeにクエスチョンマークが入る

PythonでKeras+TensorFlowで色々やってまして

input_tensor = Input(shape=(15, 15)) 

とやりまして、input_tensorについて調べると、

Tensor(“input_1:0”, shape=(?, 15, 15), dtype=float32)

となってます。

ふむふむ。Tensor型なのはわかった…。でもshapeにある「はてなマーク」は一体??

と疑問に思いました。

ありがたいことに、StackOverflowに答えがありました。

https://stackoverflow.com/questions/40951602/what-does-the-question-mark-in-tensorflow-shape-mean/40953146#40953146

“It means that first dimension is not fixed in the graph and it can vary between run calls”

というシンプルな答えでした。

最初の次元が決まってないので、これから追加する可能性があるから、?なんだそうです。

WindowsでGitのコンフリクトをTortoise Mergeで解消する

プルリクした瞬間…Not able to merge と出てコンフリクトしまう… それはプログラマにとって悪夢の始まりですよね!!
コンフリクトを解消するのは神経も使うし、間違うと悲しいことになるので、難しい作業です。(>_<)
というわけで、なるべくこの苦痛な「コンフリクトを解消する」作業をうまくやるために、皆さんどうしていますか?

私は、GitのクライアントがSource Tree、コンフリクト解消ツールはTortoise Mergeを使っています。

コマンドラインでどうにかするのもカッコがいいとは思いますが、こういう時は視覚的にわかりやすいのが一番というのが私のポリシーです。(`・ω・´)

まずは、下準備として、TortiseMerge(トートイズマージ:実際にはTortoise SVNをダウンロード・インスコする)をインストールして使えるようにしておきます。
ココからバージョン番号が大きいのを選びましょう。↓
https://osdn.net/projects/tortoisesvn/storage/


で、ここで、

「は? TortiseMergeとか、Tortoise SVNとか旧世代の遺物じゃん!なぜ今更そんなの使うのー??そもそもGitのコンフリクトの話でなんでSVN出てくるんだよ!!」

と皆さん思われたことでしょう…。(>_<)

わかる、わかるよ…。

しかし、 TortiseMerge が意外といいんですよ。

・見やすい
・シンプル
・マージするときに重要な「あっちのも使うしこっちのも使う」「こっちのだけ使う」「あっちのを使う」などがボタン操作で簡単
・SVNと関係なく、 Tortise Merge は使える
・無料

そして、最大の理由が私は高解像度モニターを使っているので、WinMergeだと設定などが消えてしまったり字がちいさすぎて使いづらい(>_<) 
ここは最終的には好みもあるでしょうから、高解像度モニタではない人は、WinMergeでもいいんじゃないでしょうか。

本題に戻りまして、TortiseMergeが使えるようになりましたら、SourceTreeの

「ツール」→「オプション」→「Diff」で「外部マージツール」で「TortoiseMerge」を選択しておきます。(下記図参照)

SourceTreeのマージツール設定画面

選べない場合は、「カスタム」を選択して、「Diffコマンド」のところに TortoiseMerge のexeのパス、引数に

「-base:\”$BASE\” -mine:\”$LOCAL\” -theirs:\”$REMOTE\” -merged:\”$MERGED\”」

と入れておきます。

で、コンフリクトしているファイルのサンプルとして、次のようなファイルがあるとします。

①masterブランチにあるphpinfo.phpというファイル

<?php

//テストなの? 新一ー!!
phpinfo();

//test
?>

②developブランチにあるphpinfo.phpというファイル

<?php

//テストだっちゅーの!!!
phpinfo();

//test
?>

というわけで、3行目のコメントの部分がコンフリクトですね。

まずはWeb上のGithubでdevelopからmasterにプルリクを作ります。

下記のように、コンフリクトして、そのままマージできません、とでますね。

Github プルリクエストした時にコンフリクトが生じる画面


Githubにもコンフリクト解消ツールがありますが、ここはいったんSourceTreeに戻ります。
masterブランチに切り替えて、developをマージします。

ここでも、コンフリクトが発生しています、と表示されます。

SourceTreeでのコンフリクトしているファイルの表示

この、エクスクラメーションがついているファイルを右クリックし

「競合を解決」→「外部のマージツールを起動」

とします。すると、TortiseMergeが起動します。

次のような画面になります。赤い部分がコンフリクトです。


TortiseMergeコンフリクトしているファイル

私がTortiseMergeで気に入っているのは、この下の図で赤丸で表示してある、右上の「Use ‘therirs’ text block」という機能たちなのですが、
・相手の変更を使う
・自分の変更を使う
・相手の変更を使った後に自分の変更を使う
・自分の変更を使った後に自分の変更を使う
などができます。一番下にプレビューが出ます。

Use theirsという機能のボタン

今回は、「Use theirs block then mine(相手の変更を使って、それから自分の変更を使う」を使います。

赤くなっている行を選択し、 「Use theirs block then mine 」のボタンをクリックしてください。

次のようになったら、

TortiseMerge マージした画面

これでコンフリクトを解消したことにするため、「Mark as resolved」ボタンをクリックして「Save」をクリックします。

TortiseMergeを終了して、SourceTreeに戻ります。

すると、下記の図のように、エクスクラメーションマークがなくなっています。(注意:何も変更していなくても、外部のマージツールから戻ると、このようになっているので、コンフリクトが解消されたかどうかをSourceTreeが見ているわけではなく、ただ単に 外部のマージツールから戻ると こうなってます。)

修正内容をもう一度確認し。コミットします。

SourceTreeでのマージされたファイルの表示


Githubでも確認すると、下記の図のように

Githubでマージが成功したことを表示する画面

「Successfully merged…」

となっていて、無事にマージされたことがわかります!!

やったね⊂(^-^)⊃